Tim Tim - 3 months ago 5
AngularJS Question

AngularJS : watching a particular property in an array of objects for changes in the property's value

In my custom directive, I'm adding elements to the DOM based on the number of objects in my datasource array. I need to watch a specific property in each object. As I add these elements to the DOM, I want to set up a $watch on the

checked
property of each object in the
toppings
array, but it's not working, and I don't know why. I set up a breakpoint inside the function that should be invoked when the property changes from true to false or false to true, but that function is never invoked. Is the reason obvious? I'm just learning Angular, so I could easily be making a stupid error.

$scope.bits = 66; (i.e. onions and olives)


$scope.toppings = [
{ topping: 1, bits: 2, name: 'onions' },
{ topping: 2, bits: 4, name: 'mushrooms' },
{ topping: 3, bits: 8, name: 'peppers' },
{ topping: 4, bits: 16, name: 'anchovies' },
{ topping: 5, bits: 32, name: 'artichokes' },
{ topping: 6, bits: 64, name: 'olives' },
{ topping: 7, bits: 128, name: 'sausage' },
{ topping: 8, bits: 256, name: 'pepperoni' }
]


Each object in the model gets a new
checked
property which will be true or false.

NOTE: the object array will at most contain a dozen or so items. Performance is not a concern.

link: function link(scope, iElement, iAttrs, controller, transcludeFn) {


<snip>

// At this point scope.model refers to $scope.toppings. Confirmed.

angular.forEach(scope.model, function (value, key) {

// bitwise: set checked to true|false based on scope.bits and topping.bits

scope.model[key].checked = ((value.bits & scope.bits) > 0);


scope.$watch(scope.model[key].checked, function () {
var totlBits = 0;
for (var i = 0; i < scope.model.length; i++) {
if (scope.model[i].checked) totlBits += scope.model[i].bits;
}
scope.bits = totlBits;
});

});

<snip>

Answer

The watchExpression parameter to $scope.$watch should either be a string or a function. I've not experimented extensively with this (I try and avoid explicit watches where possible) but I think it does also work when you watch 'simple' scope properties as object references, but not so well with more complex references.

I think if you supply the reference as a string, e.g. 'model[' + key + '].checked' then you may have some success (I only say this because I've done something similar with $watchCollection previously).

Alternatively you should be able to supply a function, e.g.

$scope.$watch(function() { return scope.model[key].checked; }, function() { ... });

Hope this helps!