Подтвердить что ты не робот

$ 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, и я бы хотел избежать использования этих изменений.

4b9b3361

Ответ 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/