星期四, 5月 01, 2014

[AngularJS] 常見問題 $watch 、$apply 筆記

這篇記錄一下查了一些有關$watch$apply的相關資料,
這二個東西又會牽扯到Angular data-binding的相關知識,
建議可以看這篇stackoverflow Using scope.$watch and scope.$apply 



Digest cycle and $scope

First and foremost, Angular defines a concept of a so called digest cycle. This cycle can be considered as a loop, during which Angular checks if there are any changes to all the variables monitored by all the $scopes. So if you have $scope.myVar defined in your controller, you are explicitly telling Angular to monitor (a.k.a watch) the changes on myVar.

$watch helps to listen for $scope changes

You can also create your own watches. Thus, $watch helps you to run some code when some value attached to the $scope has changed. It is rarely used, but sometimes is helpful. For instance, if you want to run some code each time 'myVar' changes, you could do the following:
function MyController($scope) {

   $scope.myVar = 1;

   $scope.$watch('myVar', function() {
       alert('hey, myVar has changed!');
   });

   $scope.buttonClicked = function() {
      $scope.myVar = 2; // This will trigger $watch expression to kick in
   };

}

$apply enables to integrate changes with the digest cycle

You can think of the $apply function as of an integration mechanism. You see, each time you change somevariable attached to the $scope object directly, Angular will know that the change has happened. This is because Angular already knew to monitor those changes. So if it happens in code managed by the framework, the digest cycle will carry on. However, sometimes you want to change some value outside of the Angular world and see the changes propagate normally. Consider this - you have a $scope.myVar value which will be modified within a jQuery's $.ajax() handler. This will happen at some point in future. Angular can't wait for this to happen, since it hasn't been instructed to wait on jQuery. To tackle this, $apply has been introduced. It lets you to start the digestion cycle explicitly. However, you should only use this to migrate some data to Angular (integration with other frameworks), but never use this method combined with regular Angular code, as Angular will throw an error then.

How all of this is related to DOM?

Well, you should really follow the tutorial again, now that you know all this. The digest cycle will make sure that the UI and the JS code stays synced, by evaluating every watcher attached to the all $scopes as long as nothing changes. If no more changes happen in the digest loop, then it's considered to be finished. You can attach objects to the $scope object either explicitly in the Controller, or by declaring them in {{expression}} form directly in the view.
Hope that helps to clarify some basic knowledge about all this.

那什麼時候會用到$apply?

基本上像是ng-click這些核心的方法都會自動呼叫$apply方法,簡言之當例屬angular管轄的範圍,就是需要自行呼叫$apply的驅動digest cycle。
如以下範例:
var app = angular.module('testApp', []);

function testCtrl($scope) {
    $scope.hello = 0;

    var t = setTimeout( function() { 
        $scope.hello++;
        console.log($scope.hello); 
    }, 5000);

    $scope.$watch('hello', function() { console.log('watch!'); });
}
因為setTimeout不在AngularJS管的範圍,所以$watch無法得知$scope.hello被變更,所以可以呼叫$apply
var t = setTimeout( function() { 
    $scope.hello++;
    console.log($scope.hello); 
    $scope.$apply();
}, 5000);
不過Angular已經有提供覆寫setTimeout的$timeout的模組了,建議改用以下方法
function testCtrl($scope, $timeout) {
    $scope.hello = 0;

    var t = $timeout( function() { 
        $scope.hello++;
        console.log($scope.hello); 
    }, 5000);

    $scope.$watch('hello', function() { console.log('watch!'); });
}


Using scope.$watch and scope.$apply 
[AngularJS系列(3)] View-Model双向绑定背后的故事~
Basic $watch not working in AngularJS

沒有留言:

張貼留言

留個話吧:)

其他你感興趣的文章

Related Posts with Thumbnails