Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does my Angular function is called in infinite loop?

In my application, I have an input field, and if the user enters a specific string (basically a string that matches a regular expression), a div is displayed.

The (simplified) HTML part:

<div ng-app>
  <h2>Todo</h2>
  <div ng-controller="TodoCtrl">
      <input type="text" ng-model="searchText"/>
      <div>Hello</div>
      <div ng-show="isValid(searchText)">World !</div>
  </div>
</div>

and my controller :

function TodoCtrl($scope) {
    var reg = /20\d{2}/g;    
    $scope.isValid = function(str) {
        console.log('Is valid?');
        return reg.test(str);
    }
}

When the user enters a year (thus validated by the regular expression), I get the following error:

Error: [$rootScope:infdig] 10 $digest() iterations reached. Aborting!
Watchers fired in the last 5 iterations: [["isValid(searchText); newVal: true; oldVal: false"],["isValid(searchText); newVal: false; oldVal: true"],["isValid(searchText); newVal: true; oldVal: false"],["isValid(searchText); newVal: false; oldVal: true"],["isValid(searchText); newVal: true; oldVal: false"]]
http://errors.angularjs.org/1.2.1/$rootScope/infdig?p0=10&p1=%5B%5B%22isVal…2isValid(searchText)%3B%20newVal%3A%20true%3B%20oldVal%3A%20false%22%5D%5D
    at http://cdnjs.cloudflare.com/ajax/libs/angular.js/1.2.1/angular.js:78:12
    at Scope.$digest (http://cdnjs.cloudflare.com/ajax/libs/angular.js/1.2.1/angular.js:11472:19)
    at Scope.$apply (http://cdnjs.cloudflare.com/ajax/libs/angular.js/1.2.1/angular.js:11682:24)
    at HTMLInputElement.listener (http://cdnjs.cloudflare.com/ajax/libs/angular.js/1.2.1/angular.js:15653:13)
    at http://cdnjs.cloudflare.com/ajax/libs/angular.js/1.2.1/angular.js:2562:10
    at Array.forEach (native)
    at forEach (http://cdnjs.cloudflare.com/ajax/libs/angular.js/1.2.1/angular.js:300:11)
    at HTMLInputElement.eventHandler (http://cdnjs.cloudflare.com/ajax/libs/angular.js/1.2.1/angular.js:2561:5) 

Here is a link to a JsFiddle: http://jsfiddle.net/U3pVM/6598/

What is wrong with my code, and how to enhance it?

like image 202
Romain Linsolas Avatar asked Feb 13 '23 14:02

Romain Linsolas


2 Answers

It's the g at the end of reg. When you test the same expression on the same string twise, the second time it searches after what was found the first time. So, if you start with "2001.", the function will return true, then, called again, search the remaining "." and return false.

Here is another answer, explaining how it works.

like image 107
Karolis Juodelė Avatar answered Feb 15 '23 05:02

Karolis Juodelė


You should use $watch and a scope variable, instead of using the return value of a function to decide whether to show the element.

What happens is that this line:

reg.test(str)

Makes a change in str which is a reference to the scope variable, which triggers another digest, causing an infinite loop. Edit: this is due to the regex having the global flag, as mentioned in another answer. So that combined with how angular's digest cycle works is causing it.

Here's the solution using $watch:

function TodoCtrl($scope) {
    var reg = /20\d{2}/g;    
    var isValid = function(str) {
        $scope.show = reg.test(str);
    }
    $scope.$watch('searchText',isValid);

}

fiddle

like image 39
Mosho Avatar answered Feb 15 '23 05:02

Mosho