137
votes

I have the following code which repeats and displays the name of the user and his score:

<div ng-controller="AngularCtrl" ng-app>
  <div ng-repeat="user in users | orderBy:predicate:reverse | limitTo:10">
    <div ng-init="user.score=user.id+1">
        {{user.name}} and {{user.score}}
    </div>
  </div>
</div>

And the corresponding angular controller.

function AngularCtrl($scope) {
    $scope.predicate = 'score';
    $scope.reverse = true;
    $scope.users = [{id: 1, name: 'John'}, {id: 2, name: 'Ken'}, {id: 3, name: 'smith'}, {id: 4, name: 'kevin'}, {id: 5, name: 'bob'}, {id: 6, name: 'Dev'}, {id: 7, name: 'Joe'}, {id: 8, name: 'kevin'}, {id: 9, name: 'John'}, {id: 10, name: 'Ken'}, {id: 11, name: 'John'}, {id: 1, name: 'John'}, {id: 2, name: 'Ken'}, {id: 3, name: 'smith'}, {id: 4, name: 'kevin'}, {id: 5, name: 'bob'}, {id: 6, name: 'Dev'}, {id: 7, name: 'Joe'}, {id: 8, name: 'kevin'}, {id: 9, name: 'John'}, {id: 10, name: 'Ken'}]
}

When I run the above code, I get the Error: 10 $digest() iterations reached. Aborting! error in my console.

I have created jsfiddle for same.

The sort predicate is being initialized only inside the ng-repeat and also the limit is being applied on the number of objects. so I feel having both the sortby and limitTo watchers together is the reason for error.

If the $scope.reverse is false (ascending order of score), then it does not error.

Can anyone help me understand what is wrong here? Much appreciate your help.

13
If you remove the if statement, does it still error?Mathew Berg
Thanks for your response Mathew! I diagnosed the issue wrongly. The issue seem to be with sortby and limitTo filters. I have updated the question with JSFiddle. Much appreciate your help.Chubby Boy
this is a angular thing. You need to memoize you functions and get then to remember the state. Catch my answer on stackoverflow.com/questions/14376879/…Julio Marins

13 Answers

117
votes

Please check this jsFiddle. (The code is basically the same you posted but I use an element instead of the window to bind the scroll events).

As far as I can see, there is no problem with the code you posted. The error you mentioned normally occurs when you create a loop of changes over a property. For example, like when you watch for changes on a certain property and then change the value of that property on the listener:

$scope.$watch('users', function(value) {
  $scope.users = [];
});

This will result on an error message:

Uncaught Error: 10 $digest() iterations reached. Aborting!
Watchers fired in the last 5 iterations: ...

Make sure that your code doesn't have this kind of situations.

update:

This is your problem:

<div ng-init="user.score=user.id+1"> 

You shouldn't change objects/models during the render or otherwise, it will force a new render (and consequently a loop, which causes the 'Error: 10 $digest() iterations reached. Aborting!').

If you want to update the model, do it on the Controller or on a Directive, never on the view. angularjs documentation recommends not to use the ng-init exactly to avoid these kinds of situations:

Use ngInit directive in templates (for toy/example apps only, not recommended for real applications)

Here's a jsFiddle with a working example.

9
votes

The cause of this error for me was...

ng-if="{{myTrustSrc(chat.src)}}"

in my template

It causes the function myTrustSrc in my controller to be called in an endless loop. If I remove the ng-if from this line, then the problem is solved.

<iframe ng-if="chat.src" id='chat' name='chat' class='chat' ng-src="{{myTrustSrc(chat.src)}}"></iframe>

The function is only called a few times when ng-if isn't used. I still wonder why the function is called more than once with ng-src?

This is the function in the controller

$scope.myTrustSrc = function(src) {
    return $sce.trustAsResourceUrl(src);
}
7
votes

For me it was that I was passing a function result as 2-way binding input '=' to a directive that was creating a new object every time.

so I had something like that:

<my-dir>
   <div ng-repeat="entity in entities">
      <some-other-dir entity="myDirCtrl.convertToSomeOtherObject(entity)"></some-other-dir>
   </div>
</my-dir>

and the controller method on my-dir was

this.convertToSomeOtherObject(entity) {
   var obj = new Object();
   obj.id = entity.Id;
   obj.value = entity.Value;
   [..]
   return obj;
}

which when I made to

this.convertToSomeOtherObject(entity) {
   var converted = entity;
   converted.id = entity.Id;
   converted.value = entity.Value;
   [...]
   return converted;
}

solved the problem!

Hopefully this will help someone :)

5
votes

I have another example of something that caused this. Hopefully it helps for future reference. I'm using AngularJS 1.4.1.

I had this markup with multiple calls to a custom directive:

<div ng-controller="SomeController">
    <myDirective data="myData.Where('IsOpen',true)"></myDirective>
    <myDirective data="myData.Where('IsOpen',false)"></myDirective>
</div>

myData is an array and Where() is an extension method that iterates over the array returning a new array containing any items from the original where the IsOpen property matches the bool value in the second parameter.

In the controller I set $scope.data like this:

DataService.getData().then(function(results){
    $scope.data = results;
});

Calling the Where() extension method from the directive like in the above markup was the problem. To fix this issue I moved the call to the extension method into the controller instead of the markup:

<div ng-controller="SomeController">
    <myDirective data="openData"></myDirective>
    <myDirective data="closedData"></myDirective>
</div>

and the new controller code:

DataService.getData().then(function(results){
    $scope.openData = results.Where('IsOpen',true);
    $scope.closedData = results.Where('IsOpen',false);
});
2
votes

Pretty late to the party but my issue was happening because there is a defect in ui-router in angular 1.5.8. A thing to mention is that this error appeared only the first time I was running the application and it would not reoccur afterward. This post from github solved my issue. Basically the error involves $urlRouterProvider.otherwise("/home") The solution was a workaround like this:

$urlRouterProvider.otherwise(function($injector, $location) {
   var $state = $injector.get("$state");
   $state.go("your-state-for-home");
});
2
votes

For starters ignore all answers with tell you to use $watch. Angular works off of a listener already. I guarantee you that you are complicating things by merely thinking in this direction.

Ignore all answers that tell you to user $timeout. You cannot know how long the page will take to load, therefore this is not the best solution.

You only need to know when the page is done rendering.

<div ng-app='myApp'>
<div ng-controller="testctrl">
    <label>{{total}}</label>
    <table>
      <tr ng-repeat="item in items track by $index;" ng-init="end($index);">
        <td>{{item.number}}</td>
      </tr>
    </table>
</div>

var app = angular.module('myApp', ["testctrl"]);
var controllers = angular.module("testctrl", []);
 controllers.controller("testctrl", function($scope) {

  $scope.items = [{"number":"one"},{"number":"two"},{"number":"three"}];

  $scope.end = function(index){
  if(index == $scope.items.length -1
        && typeof $scope.endThis == 'undefined'){

            ///  DO STUFF HERE
      $scope.total = index + 1;
      $scop.endThis  = true;
 }
}
});

Track the ng-repeat by $index and when the length of array equals the index stop the loop and do your logic.

jsfiddle

1
votes

I got this error in the context of angular tree control. In my case it was the tree options. I was returning treeOptions() from a function. It was always returning the same object. But Angular magically thinks that its a new object and then cause a digest cycle to kick off. Causing a recursion of digests. The solution was to bind the treeOptions to scope. And assign it just once.

1
votes

It's weird ... I've got the exact same error, coming from a different thing. When I create my controller I passed the $location parameter, like this :

App.controller('MessageController', function ($scope, $http, $log, $location, $attrs, MessageFactory, SocialMessageFactory) {
   // controller code
});

This was proven to be a bug when we use third party libraries or pure JS to manipulate some specifics (here window.location) the next digest of angular will blow this error.

So I simply removed the $location from the controller creation parameter, and it worked again, without this error. Or if you absolutely need to use the $location from angular, you have to remove every single <a href="#">link</a> in the links of your template page, and rather write href="". Worked for me.

Hope it can help one day.

0
votes

If you use angular remove the ng-storage profile from your browser console. It is not a general solution bit It worked in my case.

In Chrome F12->Resources->Local Storage

0
votes

This happened to me right after upgrading Firefox to version 51. After clearing the cache, the problem has gone.

0
votes

i had the similar error, because i had defined

ng-class="GetLink()"

instead of

ng-click="GetLink()"

0
votes

This happened to me after upgrading from angular 1.6 -> 1.7 when using $sce.trustAsResourceUrl() as the return value of a function called from ng-src. You can see this issue mentioned here.

In my case I had to change the following.

<source ng-src="{{trustSrc(someUrl)}}" type='video/mp4' />
trustSrc = function(url){
    return $sce.trustAsResourceUrl(url);
};

to

<source ng-src='{{trustedUrl}} type='video/mp4' />
trustedUrl = $sce.trustAsResourceUrl(someUrl);
0
votes

I got the exact same error using AngularJS 1.3.9 when I, in my custom sort-filter invoked Array.reverse()

After I removed it, it wa sall good.