Rishabh Rishabh - 4 months ago 59
Javascript Question

$watch or $watchCollection not working for object which is referenced by ng-model inside ng-repeat

I have defined an items object inside the main app controller which has 3 Boolean items. I am sending this object to a directive which has isolated scope. In this directive, I am iterating over the received object with ng-repeat and creating check-boxes with ng-model = item in items object.

But I am not able to watch the changes on items object (done by ng-model on the directive). The $watchCollection which I have put doesn't seem to work.

JavaScript:

var myApp = angular.module('myApp', []);

myApp.controller('MyCtrl', function($scope) {
$scope.items = {
item1: true,
item2: false,
item3: true
};

$scope.$watchCollection('items', function(newVal, oldVal) {
console.log(oldVal, newVal);
});

});

myApp.directive('testDirective', function() {
return {
restrict: 'E',
replace: true,
scope: {
items: '='
},
template: "<div ng-repeat='(item,val) in items'><label><input type='checkbox' ng-model='val'/>{{item}}</label></div>"
}
});


HTML:

<div ng-controller="MyCtrl">
<test-directive items="items"></test-directive>
</div>


However, if inside the directive HTML, I don't use ng-repeat and create individual check-boxes manually, then $watchCollection correctly watches the changes:

<div>
<label>
<input type='checkbox' ng-model='items.item1' />Item1</label>
<label>
<input type='checkbox' ng-model='items.item2' />Item2</label>
<label>
<input type='checkbox' ng-model='items.item3' />Item3</label>
</div>


Below is the JSFiddle link of the problem:
https://jsfiddle.net/rishabh1990/0kb6tezd/

Answer

$watchCollection will work only if your collection's data changes ( added, removed), not if the inner data changes.

For watching the inner data changes, you msut write

  $scope.$watch('items', function(newVal, oldVal) {
    console.log(oldVal, newVal);
  }, true);

EDITED See here

https://jsfiddle.net/5bf3v0xj/

Why

 $scope.items = [
    {name: 'item1', value: true},
    {name: 'item2', value: false},
    {name: 'item3', value: true}
  ];

and not a

 $scope.items = {
        item1: true},
        item2: false},
        item3: true}
      }

EXPLANATION

Because ng-repeat creates it's own scope and copies the primitives values.

string behave as a primitive value, so ng-repeat will have it's own copies of this variables.And because you are watching to the originals ` not copies, it will never fire.

And one more thing: it is a good style to have a . in the ng-repeat's variable :
like ng-model='item.value in the code.