Aliasger Aliasger - 1 year ago 115
AngularJS Question

Digest cycle error when passing items to ng-options from a different directive in Angular 1.5.3

I have a parent directive that has a function in the scope that will be returning an array of values. The child directive is then placed inside the template of the parent directive, and as such, is passed items as an attribute. The items are passed by the function getItems.

What is throwing me off is the fact that I get "Error: $rootScope:infdig
Infinite $digest Loop" as below:

Error: [$rootScope:infdig]

I also noticed that the the link watch gets called a lot many times - with different old and new values. I am still trying to figure that one out.

Here is the codepen link if you want to see the results i am getting: Codepen - open developer tools and you should see.

Parent Directive

app.directive('parentDirective', function(){
return {
template: '<child-directive items="ct.getItems()"></child-directive>',
controller: 'mainCtrl',
controllerAs: 'ct',
scope: {}
};
});


Parent Controller

app.controller('mainCtrl', function($scope){
this.getItems = function(){
return [
{'name': 'toronto'},
{'name': 'chicago'},
{'name': 'new york'}
];
}
});


Child Directive

app.directive('childDirective', function(){
return {
template: '<select ng-model="selectedAction" ng-options="x.name for x in ::items"></select>',
controller: 'childCtrl',
scope: {
items: '<'
},
link: function(scope, element, attrs) {
scope.$watch("items",function(newValue,oldValue) {
//This gets called when data changes.
if(newValue !== oldValue) {
console.log("--diff--");
console.dir(newValue);
console.dir(oldValue);
}
});
}
}});


Child Controller

app.controller('childCtrl', function($scope){
$scope.name = "Child controller";
});

Answer Source

Every time Angular calls the following:

items="ct.getItems()"

You are returning an entirely new array:

this.getItems = function(){
    return [
    {'name': 'toronto'},
    {'name': 'chicago'},
    {'name': 'new york'}
  ];
 }

Your watcher is watching the array reference, so it is constantly seeing a new value:

scope.$watch("items",function(newValue,oldValue) { ... 

If the array hasn't changed, just return the same array:

  var items = [
      {'name': 'toronto'},
      {'name': 'chicago'},
      {'name': 'new york'}
  ];

  this.getItems = function(){
      return items;
  }

Note that the watcher will only notice a change if the array reference changes. If you need to detect if any item reference inside the array has changed, look into $watchCollection.

Recommended from our users: Dynamic Network Monitoring from WhatsUp Gold from IPSwitch. Free Download