I'm using angularJS 1.4.8, yesterday i noticed that the $scope.$watch doesn't trigger on every change which caused bug in my application.
is there a way to force it to work on every change immediately ? like in this code, in every change on message i want the function in watch to trigger:
(function(){
angular.module('myApp', [])
.controller('myAppController', myAppController)
function myAppController($scope){
console.log('controller loading !');
$scope.message = 'message1';
$scope.$watch('message', function(newMessage){
console.log('newMessage', newMessage)
});
function changeMessage(){
$scope.message='hi';
$scope.message='hi12';
}
changeMessage();
}
})();
the console will print:
controller loading !
newMessage hi22
plunker link https://plnkr.co/edit/SA1AcIVwr04uIUQFixAO?p=preview
edit: I would really like to know if there are any other ways than wrapping the change with timeout and using scope apply, in my original code iv'e multiple places where i change the scope property and i would like to avoid using this every change.
This happens because the watch will only be triggered if the value is changed "between" digest loops.
Your function is changing the message value on the scope in the same function. This will be executed in the same digest loop.
When angular moves on to the next loop it will only see the last changed value which in your case will be hi22
.
Here's a great article which makes this behaviour clear
update your changeMessage function so that it uses $scope.$apply function which will ensure that your changes are reflected and angular is aware of your changes to the variable.
changeMessage() {
setTimeout(function () {
$scope.$apply(function () {
$scope.message = "Timeout called!";
});
}, 2000);
}
If you change value into the same digest cycle the watcher is not triggered and last value is taken. When we run $timeout
, we change $scope.message
value in next digest cycle and watcher catches it as expected.
Take look on simple test:
$scope.$watch(function(){
console.log('trigger');
return $scope.message;
},
function(newMessage){
console.log('newMessage', newMessage)
});
function changeMessage(){
$scope.message='hi';
$timeout(function(){
$scope.message='hi12';
});
}
Output:
controller loading !
trigger
newMessage hi
trigger
trigger
newMessage hi12
trigger
There is no need to wrap changeMessage in setTimeout and $apply at the same time. If you need to skip some time before execution, just use:
function changeMessage(){
$timeout(function(){
$scope.message = 'message';
}/* or add time here, doesn't matter */);
}
Or just:
function changeMessage(){
$scope.message = 'message';
$scope.$apply();
}
Both methods calls $rootScope.$digest
in the end. Here is more information: https://www.codingeek.com/angularjs/angular-js-apply-timeout-digest-evalasync/
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With