Reputation: 21067
In AngularJS I have a directive that watches a scope variable. When the variable contains certain data then I need to alter that variable a bit. The problem is that when I change the variable that my $watch
is triggered again. So I end up in a continuous loop.
scope.$watch('someVar', function(newValue, oldValue) {
console.log(newValue);
scope.someVar = [Do something with someVar];
});
This keeps triggering $watch
again, which makes sense. But I do need a way to change the watched variable. Is there a way to do this?
Upvotes: 29
Views: 75558
Reputation: 52867
When a variable is watched for changes using $scope.$watch
, angular checks if the reference has changed. If it has, then the $watch
handler is executed to update the view.
If you plan on changing the scope variable within the $watch handler, it will trigger an infinite $digest loop because the scope variable reference changes every time that it is called.
The trick to getting around the infinite digest issue is to preserve the reference inside your $watch
handler using angular.copy (docs):
scope.$watch('someVar', function(newValue, oldValue) {
console.log(newValue);
var someVar = [Do something with someVar];
// angular copy will preserve the reference of $scope.someVar
// so it will not trigger another digest
angular.copy(someVar, $scope.someVar);
});
Note: This trick only works for object references. It will not work with primitives.
In general, its not a good idea to update a $watched
variable within its own $watch
listener. However, sometimes it may be unavoidable.
Upvotes: 38
Reputation: 2276
Inside function use if condition to avoide continues for loop
scope.$watch('someVar', function(newValue, oldValue) {
if(newValue!==oldValue) {
console.log(newValue);
scope.someVar = [Do something with someVar];
}
});
Upvotes: 17
Reputation: 18065
you can manage it using a bool variable
$scope.someVarChanged = false;
scope.$watch('someVar', function(newValue, oldValue) {
console.log(newValue);
$scope.someVarChanged = !$scope.someVarChanged!;
if($scope.someVarChanged) {
scope.someVar = [Do something with someVar];
}
});
Upvotes: 2
Reputation: 4611
yes you can cancel it like this
var wathcer = scope.$watch('someVar', function(newValue, oldValue) {
console.log(newValue);
scope.someVar = [Do something with someVar];
});
wathcer(); // clear the watch
Upvotes: 1
Reputation: 17878
This is how dirty-checking works. Every time something on the $scope
changes Angular will spin through everything that is attached to the scope and it will keep doing so untill there are no more changes.
If you want to do something like that, you will have to make sure that your $watch
function is idempotent. You will have to look at both newValue
and oldValue
and figure out wether you've already applied the changes to your variable in this $digest
loop. How you can do that depends a bit on what kind of changes you're doing to someVar
.
Generally speaking, changing a watched variable in a watch-function is unfortunately not a good idea.
Upvotes: 2