Error: 10 $digest() iterations reached. Aborting! with dynamic sortby predicate
Asked Answered
G

13

145

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.

Gao answered 17/1, 2013 at 10:23 Comment(3)
If you remove the if statement, does it still error?Milburt
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.Gao
this is a angular thing. You need to memoize you functions and get then to remember the state. Catch my answer on #14377379Kilburn
F
119

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.

Fiftyfifty answered 17/1, 2013 at 14:37 Comment(5)
FYI, instead of angular.element(w).bind() you can use element.bind().Glairy
Thanks much bmleite and Mark. I diagnosed the issue wrongly. Please see my comments above. I have updated the question with JSFiddle. Much appreciate your help.Gao
Thanks very much @bmleite! Just trying to understand more. But why is that if $scope.reverse=false (ascending order of score) does not cause error?Gao
For ascending and descending you should use the prefixes '+' and '-', respectively (i.e. '+score' and '-score'). More information on this here.Fiftyfifty
This answer solved my problem. The explaination is good in that it describes the problem: the $digest() iterations error results when you attempt to modify a property at the same time you are looping through. So each property change triggers a update of the view (UI) and angular eventually maxes out at 10 loops. The jsFiddle example puts the property change in the Controller. Changing the property in the View results in this error.Mantic
S
10

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);
}
Sergu answered 6/11, 2014 at 15:57 Comment(3)
I still wonder why the function is called more than once with ng-src? - because angular tries to create the html a few times until it gets 2 results that are identical. that is the meaning of the error 10 $digest() iterations reached, he tried 10 times and never got 2 identical resultsCardiganshire
@Cardiganshire could you provide a link with this information? Thanks.Delusion
@Delusion look here docs.angularjs.org/api/ng/type/$rootScope.Scope CTRF+F this "The rerun iteration limit is 10 to prevent an infinite loop deadlock"Cardiganshire
Z
7

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 :)

Zannini answered 23/3, 2016 at 17:41 Comment(0)
C
6

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);
});
Commendam answered 25/6, 2015 at 17:13 Comment(1)
That sounds exactly like the problem I have. A simple function that did not update anything, but created a local array, iterated over one existing $scope array, and created objects in the local array, and returned. By calling once and storing it in a $scope member it solve the problem. Thanks. Wish I knew what the problem with using the method directly is.Cystotomy
K
2

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

Kenny answered 20/9, 2016 at 17:6 Comment(0)
A
2

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");
});
Actomyosin answered 9/3, 2018 at 15:20 Comment(0)
B
1

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.

Byrle answered 25/6, 2015 at 12:38 Comment(0)
J
1

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.

Janice answered 30/4, 2018 at 15:25 Comment(0)
H
0

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

Hadria answered 27/7, 2016 at 11:55 Comment(0)
L
0

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

Livingston answered 25/1, 2017 at 8:48 Comment(0)
O
0

i had the similar error, because i had defined

ng-class="GetLink()"

instead of

ng-click="GetLink()"

Orella answered 11/3, 2017 at 10:7 Comment(0)
A
0

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);
Alignment answered 9/10, 2018 at 15:32 Comment(0)
P
0

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.

Purgatorial answered 26/11, 2019 at 11:34 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.