I believe I have a problem that may be fairly easily addressed via something that I am missing, but I can't seem to see what the actual issue is. I have an application that returns 5000 points (5 array elements of 1000 x,y points) every second that I want to update on the client side using NVD3. This is an AngularJS application, so I am using krispos angular-nvd3
directive. However, it is bogging the whole application down, and it appears that, according to the timeline captured by Chrome's developer tools, the application seems to be waiting on d3_timer_step
to return for 5-6 seconds.
I thought this problem was due to how we were updating the data, but the whole issue seems to be with the actual d3 portion. The code on the client side is
<nvd3 options="optionsRingdown" data="ringdownAvg" config="{refreshDataOnly:true}"></nvd3>
and in the controller the options are defined as follows
$scope.options = {
chart: {
type: 'lineChart',
height: 300,
margin: {
top: 20,
right: 40,
bottom: 60,
left: 75
},
x: function(d) {
return d.x;
},
y: function(d) {
return d.y;
},
useInteractiveGuideline: false,
yAxis: {
tickFormat: function(d) {
return d3.format('0.01f')(d);
},
axisLabel: 'Testing'
},
xAxis: {
tickFormat: function(d) {
return d3.time.format('%X')(new Date(d));
},
rotateLabels: -45
},
transitionDuration: 0,
showXAxis: true,
showYAxis: true
}
};
and the data is defined in the following template
var ringdownT = [{
values: [],
key: 'Cell 0'
}, {
values: [],
key: 'Cell 1'
}, {
values: [],
key: 'Cell 2'
}, {
values: [],
key: 'Cell 3'
}, {
values: [],
key: 'Cell 4'
}];
The data is updated via a function call on broadcast from a service using the following
function updateCRD(d){
var dataOut = {
"tauData": [],
"rdFit": ringdownT,
"rdAvg":ringdownT
}
for (k = 0; k < d.cell.length; k++) {
dataOut.rdAvg[k].values = d.cell[k].avg_rd;
dataOut.rdFit[k].values = d.cell[k].fit_rd;
}
return dataOut;
}
The function is called in a broadcast using the following (which is broadcast at 1 second intervals)
$scope.$on('dataAvailable', function() {
$scope.data = Data.crd;
var data = updateCRD(Data.crd);
$scope.tauData = data.tauData;
$scope.ringdownAvg = data.rdAvg;
$scope.ringdownFit = data.rdFit;
});
Does anyone see something that looks obviously wrong here or that I should be doing differently? Is there an option that I am missing? Any help would be great.
Cheers, Matt
Try to add deepWatchData: false
flag to config (it means that directive won't watch the data for updates) and update chart via api
:
<nvd3 options="optionsRingdown" data="ringdownAvg" api="apiRingdown" config="{refreshDataOnly:true, deepWatchData: false}"></nvd3>
The directive watches options and complex data objects for any updates using $watch(watchExpression, listener, [objectEquality])
method. In our case deepWatchData
is the objectEquality
flag, while watching chart data for updates.
According to the angular docs, inequality of the watchExpression is determined according to the angular.equals function. And to save the value of the object for later comparison, the angular.copy function is used. This therefore means that watching complex objects will have adverse memory and performance implications.
In versions (1.0.2, 1.0.3) only, this flag is false
by default.
Then, to update chart, we can use apiRingdown.update
method in your controller:
$scope.$on('dataAvailable', function() {
$scope.data = Data.crd;
var data = updateCRD(Data.crd);
$scope.tauData = data.tauData;
$scope.ringdownAvg = data.rdAvg;
$scope.ringdownFit = data.rdFit;
//this line updates the chart
$scope.apiRingdown.update();
});
UPDATED
Some updates are added in the latest versions [1.0.4+]. Now flag deepWatchData
means to use or not to use data watching at all (it's not objectEquality
as before). And deepWatchData
is true
by default. But now we can manage the $watch
depth with a new flag deepWatchDataDepth: 2
, and thereby regulate performance. With this flag we can specify a change detection strategy (scope $watch depth) for data:
0 - By Reference (the least powerful, but the most efficient)
1 - By Collection Items
2 - By Value (the most powerful, but also the most expensive; default value)
Also, flag refreshDataOnly
is true
by default.
So, the updated tag element may look like:
<nvd3 options="optionsRingdown" data="ringdownAvg" api="apiRingdown" config="{deepWatchDataDepth: 0}"></nvd3>