Reputation: 2028
Assuming the following JSFiddle: https://jsfiddle.net/pmgq00fm/1/
I want my NVD3 chart to update in real time, based on the setInterval() on line 39 that updates the data the directive is bound to. Here are quick pointers on the architecture and code:
To debug, run the JSFiddle and open the console to see the objects and debug prints.
HTML:
<div id="stats-container" ng-app="myApp" ng-controller="ThisController">
<div id="nvd3-test" nvd3-discrete-bar data="data.graph">
</div>
JS:
myControllers.controller('ThisController', ['$scope', function ThisController($scope){
$scope.data = { graph : [ ... ] };
updateFnc = function(data){
for(var i = 0; i < data[0].values.length ; i++){
data[0].values[i].value = Math.random();
}
console.log(Date.now()/1000 + ": In Controller");
console.log(data);
};
setInterval(function(){
updateFnc($scope.data.graph);
}, 1000);
}]);
myServices.factory('NVD3Wrapper', ['NVD3S', 'D3S', '$q', '$rootScope', function NVD3Wrapper(nvd3s, d3s, $q, $rootScope){
return {
nvd3: nvd3s,
d3: d3s,
discreteBarChart : function(data, config){
var $nvd3 = this.nvd3,
$d3 = this.d3,
$nvd3w = this, //In order to resolve the nvd3w in other scopes.
d = $q.defer(); //Creating a promise because chart rendering is asynchronous
$nvd3.addGraph(function() {
...
});
return {
chart: function() { return d.promise; } //returns the chart once rendered.
};
},
_onRenderEnd: function(d, chart){
$rootScope.$apply(function() { d.resolve(chart); });
},
};
}]);
myDirectives.directive('nvd3DiscreteBar', ['NVD3Wrapper', function(nvd3w){
return {
restrict: 'EA',
scope: {
data: '=' // bi-directional data-binding
},
link: function(scope, element, attrs) {
var chart,
config = {
target: element,
};
var wrapper = nvd3w.discreteBarChart(scope.data, config);
wrapper.chart().then(function(chart){
scope.$watch(function() { return scope.data; }, function(newValue, oldValue) {
console.log(Date.now()/1000 + ": In Directive $watch");
if (newValue)
chart.update();
}, true);
});
//For testing
setInterval(function(){ console.log(Date.now()/1000 + ": In Directive"); console.log(scope.data); }, 1000);
}
};
}]);
Any help would be greatly appreciated! Thanks a lot!
EDIT: New JSFiddle with Andrew Shirley's answer: https://jsfiddle.net/pmgq00fm/3/
Upvotes: 0
Views: 345
Reputation: 417
Add the line
$scope.$apply()
to the update function. This is because when a variable is updated in the javascript, outside of the common angular functions, angular isn't aware of the change and therefore doesn't waste any effort refreshing the DOM. This forces a digest cycle which should refresh what you see.
EDIT: It is pretty important to actually understand why you're actually using a scope.apply and I don't feel I've described it incredibly well. Here's an article that does that much better than I have.
http://jimhoskins.com/2012/12/17/angularjs-and-apply.html
I point this out because you need to be aware that if you ARE within a function that's closely tied to angular (e.g. something called by ng-click), then if you attempt to use a scope.$apply you will get javascript errors, because you'll already be in the middle of a digest cycle.
Upvotes: 2