Reputation: 81627
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?
Upvotes: 4
Views: 1205
Reputation: 14640
Try move the reg variable inside the function.
function TodoCtrl($scope) {
$scope.isValid = function(str) {
var reg = /20\d{2}/g;
console.log('Is valid?');
return reg.test(str);
}
}
As pointed in the link by Karolis Juodelė, the regex stores the last valid position and the g symbol causes the second call to be false. By moving the variable locally inside the function, the reg variable is a new reference if you still want to use the g symbol.
Angular evaluate the function twice to make sure that it doesn't change, but indeed in the second call, it returns false, and then true, and then false, so forth.
Upvotes: 0
Reputation: 7078
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);
}
Upvotes: 1
Reputation: 3770
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.
Upvotes: 7