Solvedangular.js Infinite $digest() loop AngularJS with filter expression on html attribute

I'm submitting a ...

  • bug report
  • feature request
  • other (Please do not submit support requests here (see above))

Current behavior:

When I create a filter expression on an html attribute, an attribute that is later bounded incoming in a component and used inside an ng-repeat, it triggers an infinite digest cycle/loop. The output comes up fine on the screen, but there are errors in the console suggesting that it triggers and infinite digest cycle. Code to reproduce is on a plnkr: http://plnkr.co/edit/JdiLEIyji2pHd3eeNMUL?p=preview.
This also happens when the array I'm passing is an array of literals such as number or string (http://plnkr.co/edit/mfjPgZjBro39Nb7WYpjU?p=preview).

Expected / new behavior:

  1. If it is a bug, then the behaviour without the bug would not result in an infinite digest cycle, and it would improve readability / seperation of concerns. As the component I'm passing in the filtered array only needs to take care of how it's showing the items in the array, not extra filter params.
  2. If it isn't a bug, then I feel documentation needs to be changed as now I feel the error message is unclear to me, and the docs now ways it is possible to use filter expressions inside those places..

Minimal reproduction of the problem with instructions:

Here's a plnkr with the code in action http://plnkr.co/edit/JdiLEIyji2pHd3eeNMUL?p=preview - open the devtools in your browser to see the errors appear.

Angular version: 1.5.* and 1.6.1/2/3

Browser: [all]

Anything else:

Error message

Error: [$rootScope:infdig] http://errors.angularjs.org/1.6.3/$rootScope/infdig?p0=10&p1=%5B%5B%7B%22ms…e%2C%22type%22%3A1%2C%22%24%24hashKey%22%3A%22object%3A5%22%7D%5D%7D%5D%5D
    at eval (angular.js:38)
    at m.$digest (angular.js:18048)
    at m.$apply (angular.js:18280)
    at eval (angular.js:1912)
    at Object.invoke (angular.js:5003)
    at c (angular.js:1910)
    at Object.Pc [as bootstrap] (angular.js:1930)
    at execute (VM877 main.ts!transpiled:21)
    at f (system.js:5)
    at Object.execute (system.js:5)

Possible solutions

  1. In the repo where I had the problem at first: I did <todo-list todo-items="$ctrl.todoItems" filter-by="{completed:true}"></todo-list>. For full source see here: aredfox/todo-angularjs-typescript@e71900b. But I feel it's working around the problem, plus I don't understand why this triggers a $digest() cycle and why it shouldn't just work.

Related issue

  1. StackOverflow post of this issue: http://stackoverflow.com/questions/43119977/infinite-digest-loop-angularjs-with-filter-expression-on-html-attribute
24 Answers

✔️Accepted Answer

OK, so the filter always returns a new object and the one-way binding only tests for object identity equivalence. So it is expected behaviour.

Here is a reasonable workaround (IMO), which could be tailored to the specific needs of the situation for best performance: https://plnkr.co/edit/g0AzLUAbwfiiIl8PNgcE?p=preview

<comp items="$ctrl.items | filter: $ctrl.filterBy | stable"></comp>
  .filter('stable', function() {
    var oldValue = NaN;
    return function(value) {
      oldValue = angular.equals(value, oldValue) ? oldValue : value;
      return oldValue;
    };

We should document this.

Other Answers:

@gkalpak - for sure, it would need tweaking for production :-)

@aredfox - There is nothing wrong with using =* as long as you don't want to ensure that changes inside the component do not appear outside the component.

I guess we are getting close to enough use cases to implement <*

Related Issues:

5
angular.js Infinite $digest() loop AngularJS with filter expression on html attribute
OK so the filter always returns a new object and the one-way binding only tests for object identity ...