i have the following directive:
app.directive('rickshawChart', function()
{
return{
$scope: {
data: '=',
renderer: '='
},
template: '<div></div>',
restrict: 'E',
link: function postLink(scope, element, attrs)
{
scope.$watchCollection('[data,renderer]', function(newVal, oldVal)
{
if(!newVal[0])
{
return;
}
var graphEl = element.find("div:first");
graphEl.html('');
var graph = new Rickshaw.Graph({
element: graphEl[0],
width: attrs.width,
height: attrs.height,
series: [{data: scope.data, color: attrs.color, name: attrs.series}],
renderer: scope.renderer
});
graph.render();
});
}
}
});
With the following html:
<rickshaw-chart
data="sightingByDate"
color="green"
renderer="renderer"
width="100%"
height="100%">
</rickshaw-chart>
Here i fill the data in with the following controller:
app.controller('CompetenceController', ['$http', '$scope','$sessionStorage','$log','Session','api', function ($http, $scope, $sessionStorage,$log, Session, api) {
$scope.result =[ { x: 0, y: 40 }, { x: 1, y: 49 }, { x: 2, y: 38 }, { x: 3, y: 30 }, { x: 4, y: 32 } ];
$scope.renderer = 'scatterplot';
$scope.sightingsByDate = _($scope.result)
.chain()
.countBy(function(sighting){
return sighting.y;
})
.pairs()
.map(function(pair){
return pair;
})
.value();
}]);
however from the debugger i am able to see that the $watchCollection
is never executed.
What am i doing wrong?
i have the following directive:
app.directive('rickshawChart', function()
{
return{
$scope: {
data: '=',
renderer: '='
},
template: '<div></div>',
restrict: 'E',
link: function postLink(scope, element, attrs)
{
scope.$watchCollection('[data,renderer]', function(newVal, oldVal)
{
if(!newVal[0])
{
return;
}
var graphEl = element.find("div:first");
graphEl.html('');
var graph = new Rickshaw.Graph({
element: graphEl[0],
width: attrs.width,
height: attrs.height,
series: [{data: scope.data, color: attrs.color, name: attrs.series}],
renderer: scope.renderer
});
graph.render();
});
}
}
});
With the following html:
<rickshaw-chart
data="sightingByDate"
color="green"
renderer="renderer"
width="100%"
height="100%">
</rickshaw-chart>
Here i fill the data in with the following controller:
app.controller('CompetenceController', ['$http', '$scope','$sessionStorage','$log','Session','api', function ($http, $scope, $sessionStorage,$log, Session, api) {
$scope.result =[ { x: 0, y: 40 }, { x: 1, y: 49 }, { x: 2, y: 38 }, { x: 3, y: 30 }, { x: 4, y: 32 } ];
$scope.renderer = 'scatterplot';
$scope.sightingsByDate = _($scope.result)
.chain()
.countBy(function(sighting){
return sighting.y;
})
.pairs()
.map(function(pair){
return pair;
})
.value();
}]);
however from the debugger i am able to see that the $watchCollection
is never executed.
What am i doing wrong?
Share Improve this question asked Apr 2, 2015 at 9:21 Marc RasmussenMarc Rasmussen 20.6k83 gold badges223 silver badges384 bronze badges 1- Ping check my aswer :) – squiroid Commented Apr 2, 2015 at 9:47
3 Answers
Reset to default 6You should use the $watch
expression with true
as a third parameter:
scope.$watch('[data,renderer]', function (newVal, oldVal) {
//code
}, true);
$watchCollection
doesn't work for you because you have both an array and a property. In this case is like you're checking contemporarily $scope.$watch('data'
and $scope.$watch('renderer'
. And if a property in the array changes, the event will not trigger.
There are four types of $watch
:
- the simpliest is
$watch
, it simply check if the property or the whole object changed - the second is
$watchCollection
which watches if any property within an array changed (in your case this doesn't work, because you have both an array and a property) - the third is
$watchGroup
(since 1.3) which is the most recent version of$watchCollection
, which allows you to check an array of expressions (but the same could be achieved with$watchCollection
as you were trying to do $watch('object', function(){...}, true)
. This check if every single property changed within an object
This is a simplified version of your code, if you press the change value button, you can see how an element in the array is changed and how the event is triggered in the console: http://jsfiddle/27gc71bL/
EDIT: I forgot to add something in my answer. I think there's a typo in your code, you are creating a directive using:
$scope: {
data: '=',
renderer: '='
},
This is not creating a new scope within the directive. So when you are using scope.data
in the link function, you are referring to the $scope.data
of the parent (the controller).
What I think you wanted, is to create a new scope in this way:
scope: {
data: '=',
renderer: '='
},
and, in your controller, bind the array you want to check to sightingByDate
.
As doc said
Shallow watches the properties of an object and fires whenever any of the properties change (for arrays, this implies watching the array items; for object maps, this implies watching the properties). If a change is detected, the listener callback is fired.
Watch collection is used for shallow copy of object and array only :) And in your case both things are just value not array or object.
So I guess you need $watchGroup for your work to be done.
$scope.$watchGroup(['teamScore', 'time'], function(newVal, oldVal) {.. }
The code looks correct as long as somewhere either sightingByDate or renderer is getting modified. Perhaps, this is not evident in your code snippet. Make sure any or both of these are getting changed somewhere in your flow to trigger WatchCollection