$ scope. $watch не срабатывает при каждом изменении
Я использую angularJS 1.4.8, вчера я заметил, что $scope. $watch не срабатывает при каждом изменении, которое вызвало ошибку в моем приложении.
Есть ли способ заставить его немедленно работать над каждым изменением?
как в этом коде, при каждом изменении сообщения я хочу, чтобы функция в секундах запускала:
(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();
}
})();
консоль будет печатать:
controller loading !
newMessage hi22
plunker link https://plnkr.co/edit/SA1AcIVwr04uIUQFixAO?p=preview
изменить:
Мне бы очень хотелось узнать, есть ли какие-либо другие способы, кроме переноса изменений с помощью тайм-аута и использования области применения, в моем исходном коде iv'e несколько мест, где я изменяю свойство scope, и я бы хотел избежать использования этих изменений.
Ответы
Ответ 1
обновите функцию changeMessage, чтобы она использовала функцию $scope. $apply, которая обеспечит отражение ваших изменений и angular знает о ваших изменениях в переменной.
changeMessage() {
setTimeout(function () {
$scope.$apply(function () {
$scope.message = "Timeout called!";
});
}, 2000);
}
Ответ 2
Это происходит потому, что часы будут запускаться только в том случае, если значение изменяется "между циклами дайджеста".
Ваша функция меняет значение сообщения в области в той же функции. Это будет выполнено в том же цикле дайджеста.
Когда angular переходит к следующему циклу, он увидит только последнее измененное значение, которое в вашем случае будет hi22
.
Вот отличная статья, которая делает это поведение понятным
Ответ 3
Если вы меняете значение в один и тот же цикл дайджеста, наблюдатель не запускается и последнее значение берется. Когда мы запускаем $timeout
, мы меняем значение $scope.message
в следующем цикле дайджеста, и наблюдатель ловит его, как ожидалось.
Посмотрите на простой тест:
$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';
});
}
Вывод:
controller loading !
trigger
newMessage hi
trigger
trigger
newMessage hi12
trigger
Ответ 4
$watch()
срабатывает только каждый $digest()
.
Подробное объяснение о $apply()
и $digest()
В вашем случае вы продолжаете обновлять $scope.message
в текущем цикле $digest()
.
Это можно изменить, применив каждое новое значение к $scope
с помощью $apply()
. Как @Ajinkya написал.
Единственная проблема, с установкой 2000 мс как тайм-аута, не всегда обеспечивает ее выполнение после $digest()
. Кроме того, Angular имеет встроенную функцию тайм-аута. См. Ниже.
(function(){
angular.module('myApp', [])
.controller('myAppController', myAppController)
function myAppController($scope, $timeout){
console.log('controller loading !');
$scope.message = 'message1';
$scope.$watch('message', function(newMessage){
console.log('newMessage', newMessage)
});
function changeMessage(){
setTimeout(function () {
$scope.$apply(function () {
$scope.message='hi12';
});
}, 2000);
}
changeMessage();
}
})();
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="myApp" ng-controller="myAppController"></div>
Ответ 5
Нет необходимости обертывать changeMessage в setTimeout и $apply одновременно. Если вам нужно пропустить некоторое время перед исполнением, просто используйте:
function changeMessage(){
$timeout(function(){
$scope.message = 'message';
}/* or add time here, doesn't matter */);
}
Или просто:
function changeMessage(){
$scope.message = 'message';
$scope.$apply();
}
Оба метода вызывают $rootScope.$digest
в конце. Вот дополнительная информация: https://www.codingeek.com/angularjs/angular-js-apply-timeout-digest-evalasync/