Rishabh
Rishabh

Reputation: 900

$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/

Upvotes: 1

Views: 1363

Answers (1)

Suren Srapyan
Suren Srapyan

Reputation: 68665

$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.

Upvotes: 1

Related Questions