$ scope。$ watch不会触发每次更改

时间:2017-09-05 06:32:14

标签: javascript angularjs

我正在使用angularJS 1.4.8,昨天我注意到$ scope。$ watch并没有触发每次导致我的应用程序出错的更改。

有没有办法强制它立即对每一个变化起作用? 就像在这段代码中一样,在消息的每一次更改中,我希望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多个地方,我改变范围属性,我想避免使用这每个更改

5 个答案:

答案 0 :(得分:11)

发生这种情况是因为只有在“摘要循环”之间更改了值时才会触发监视。

您的功能是在同一功能中更改示波器上的消息值。这将在同一个摘要循环中执行。 当角度移动到下一个循环时,它将只看到最后更改的值,在您的情况下将为hi22

Here's一篇很好的文章让这种行为变得清晰

答案 1 :(得分:5)

更新你的changeMes​​sage函数,使其使用$ scope。$ apply函数,这将确保您的更改得到反映,并且angular知道您对变量的更改。

changeMessage() {
   setTimeout(function () {
        $scope.$apply(function () {
          $scope.message = "Timeout called!";
        });
    }, 2000);
}

答案 2 :(得分: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

答案 3 :(得分:2)

$watch()仅在每$digest()之间触发。

Detailed explaination about the $apply() and $digest()

在您的情况下,您会不断更新当前$scope.message周期中的$digest()

您可以使用$scope将每个新值应用于$apply()来更改此设置。就像@Ajinkya写的那样。 唯一的问题是,将2000ms设置为超时,并不总是确保它在$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>
&#13;
&#13;
&#13;

解决方案

最好的方法是在$timeout函数中调用构建,而不用以毫秒为单位设置时间。

这样,角度总是确保$timeout将在最新的$digest()之后运行。最重要的是。你不必使用$scope.$apply()。因为$timeout allready运行$digest(),其中$scope.$apply()手动调用新的$diggest()周期。

&#13;
&#13;
(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(){
        $timeout(function () {
            $scope.message='hi12';
        });
      
    }
    
    changeMessage(); 
  }
  
})();
&#13;
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>

    <div ng-app="myApp" ng-controller="myAppController"></div>
&#13;
&#13;
&#13;

答案 4 :(得分:2)

无需在setTimeout和$ apply中同时包装changeMes​​sage。如果您需要在执行前跳过一段时间,只需使用:

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/