1
votes

I'am sure I am doing something wrong, but even debugging on Angular.js I can't find the mistake

I am following the angular basic tutorial here: https://docs.angularjs.org/tutorial/step_03 All examples on tutorial works fine. But I wanted to try some angular cool features and added code by myself to the examples of step 3

index.html: https://gist.github.com/tario/f07239992eea75535421

controller.js https://gist.github.com/tario/1b6155b5c97e747abe32

The filter is defined here: https://gist.github.com/tario/1b6155b5c97e747abe32#file-controllers-js-L16

This produce the following error as soon as I enable the uppercase using the checkbox:

Error: [$rootScope:infdig] 10 $digest() iterations reached. Aborting! Watchers fired in the last 5 iterations: [["fn: $watchCollectionWatch; newVal: 62; oldVal: 59"],["fn: $watchCollectionWatch; newVal: 65; oldVal: 62"],["fn: $watchCollectionWatch; newVal: 68; oldVal: 65"],["fn: $watchCollectionWatch; newVal: 71; oldVal: 68"],["fn: $watchCollectionWatch; newVal: 74; oldVal: 71"]] http://errors.angularjs.org/1.2.17/$rootScope/infdig?p0=10&p1=%5B%5B%22fn%3…20%24watchCollectionWatch%3B%20newVal%3A%2074%3B%20oldVal%3A%2071%22%5D%5D at http://xxxxxxx.doesntexists.com:8000/app/bower_components/angular/angular.js:78:12 at Scope.$get.Scope.$digest (http://xxxxxxx.doesntexists.com:8000/app/bower_components/angular/angular.js:12396:19) at Scope.$get.Scope.$apply (http://xxxxxxx.doesntexists.com:8000/app/bower_components/angular/angular.js:12622:24) at HTMLInputElement. (http://xxxxxxx.doesntexists.com:8000/app/bower_components/angular/angular.js:17016:11) at http://xxxxxxx.doesntexists.com:8000/app/bower_components/angular/angular.js:2810:10 at Array.forEach (native) at forEach (http://xxxxxxx.doesntexists.com:8000/app/bower_components/angular/angular.js:320:11) at HTMLInputElement.eventHandler (http://xxxxxxx.doesntexists.com:8000/app/bower_components/angular/angular.js:2809:5)

I don't know if doing the filter that way is the best practice, but I want to know why is failing since the only thing I am doing is create a new array with the transformed content of the other array, it's ok ???

1
If my answer was useful to you, it would be nice to accept it. Else, I would be glad to edit it if you have an issue with it. - Julien

1 Answers

0
votes

As mentionned in the documentation of Angular on this error:

This error occurs when the application's model becomes unstable and each $digest cycle triggers a state change and subsequent $digest cycle. Angular detects this situation and prevents an infinite loop from causing the browser to become unresponsive.

At each digest cycle, your filter is returning a new array and since AngularJS watches by dirty checking, it triggers a new digest cycle since the new value sent by your filter is different from the one previously sent.

I see two ways to solve your issue:

1) Send the same object if the same parameters are given by using the memoize function of Lodash.

At each call, of a memoized function, the resolver (the second function provided to memoize) will look in the cache if the result is stored or not (by default, the resolver returns the first argument converted to a string which had some gibberish appended). If a result was found, it's returned. Else, the function is computed and the return is stored in the cache.

    phonecatApp.filter('uppercaseAllThings', function () {
        return _.memoize(
        function (items, enabled) {
            if (enabled) {
                var newitems = [];
                angular.forEach(items, function (item) {
                    newitems.push(uppercaseAllAttributes(item));
                });

                return newitems;
            }
            return items;
        },
        function (items, enabled) {
            return enabled + angular.toJson(items)
        });
    });

The JsFiddle explaining this solution can be found here.

2) If all you want is set the value to uppercase, you can use the CSS to do the job by applying conditionnaly a CSS class.

The CSS class that we will conditionnally apply will be using the text-transform property

.uppercase {
   text-transform:uppercase;
}

This class must be added to your style sheet.

And the HTML will be changed from

<li ng-repeat="phone in phones | filter:query | orderBy:orderProp | uppercaseAllThings:uppercaseEnabled">
   <span>{{phone.name}}</span>
   <p>{{phone.snippet}}</p>
</li>

to

<li ng-repeat="phone in phones | filter:query | orderBy:orderProp"
    ng-class="{'uppercase':uppercaseEnabled}">
   <span>{{phone.name}}</span>
   <p>{{phone.snippet}}</p>
</li>

I've made a simple JSFiddle explaining the second solution.