angularjs基础—$scope.$apply的应用

$apply() $digest() AngularJS 中是两个核心概念

AngularJS 提供了一个非常酷的特性叫做双向数据绑定 (Two-way Data Binding) ,这个特性大大简化了我们的代码编写方式。

数据绑定意味着当 View 中有任何数据发生了变化,那么这个变化也会自动地反馈到 scope 的数据上,也即意味着 scope 模型会自动地更新。

类似地,当 scope 模型发生变化时, view 中的数据也会更新到最新的值。

那么 AngularJS 是如何做到这一点的呢?当你写下表达式如 {{ aModel }} 时, AngularJS 在幕后会为你在 scope 模型上设置一个 watcher ,它用来在数据发生变化的时候更新 view


这里的 watcher 和你会在 AngularJS 中设置的 watcher 是一样的:

$scope.$watch('aModel', function(newValue, oldValue) {
  //update the DOM with newValue
});

传入到 $watch() 中的第二个参数是一个回调函数,该函数在 aModel 的值发生变化的时候会被调用。当 aModel 发生变化的时候,这个回调函数会被调用来更新 view ,,但是,还存在一个很重要的问题! AngularJS 是如何知道什么时候要调用这个回调函数呢?即, AngularJS 是如何知晓 aModel 发生了变化,才调用了对应的回调函数呢?它会周期性的运行一个函数来检查 scope 模型中的数据是否发生了变化吗?好吧,这就是 $digest 循环的用武之地了。


$digest 循环中, watchers 会被触发。当一个 watcher 被触发时, AngularJS 会检测 scope 模型,如何它发生了变化那么关联到该 watcher 的回调函数就会被调用。那么,下一个问题就是 $digest 循环是在什么时候以各种方式开始的?

在调用了 $scope.$digest() 后, $digest 循环就开始了。假设你在一个 ng-click 指令对应的 handler 函数中更改了 scope 中的一条数据,此时 AngularJS 会自动地通过调用 $digest() 来触发一轮 $digest 循环。当 $digest 循环开始后,它会触发每个 watcher 。这些 watchers 会检查 scope 中的当前 model 值是否和上一次计算得到的 model 值不同。如果不同,那么对应的回调函数会被执行。调用该函数的结果,就是 view 中的表达式内容 ( 译注:诸如 {{ aModel }}) 会被更新。除了 ng-click 指令,还有一些其它的 built-in 指令以及服务来让你更改 models( 比如 ng-model $timeout ) 和自动触发一次 $digest 循环。

现在,假设你将 ng-click 指令关联到了一个 button 上,并传入了一个 function 名到 ng-click 上。当该 button 被点击时, AngularJS 会将此 function 包装到一个 wrapping function 中,然后传入到 $scope.$apply() 。因此,你的 function 会正常被执行,修改 models( 如果需要的话 ) ,此时一轮 $digest 循环也会被触发,用来确保 view 也会被更新。

Note: $scope.$apply() 会自动地调用 $rootScope.$digest() $apply() 方法有两种形式。第一种会接受一个 function 作为参数,执行该 function 并且触发一轮 $digest 循环。第二种会不接受任何参数,只是触发一轮 $digest 循环。我们马上会看到为什么第一种形式更好。

什么时候手动调用 $apply() 方法?

如果 AngularJS 总是将我们的代码 wrap 到一个 function 中并传入 $apply() ,以此来开始一轮 $digest 循环,那么什么时候才需要我们手动地调用 $apply() 方法呢?实际上, AngularJS 对此有着非常明确的要求,就是它只负责对发生于 AngularJS 上下文环境中的变更会做出自动地响应 ( 即,在 $apply() 方法中发生的对于 models 的更改 ) AngularJS built-in 指令就是这样做的,所以任何的 model 变更都会被反映到 view 中。但是,如果你在 AngularJS 上下文之外的任何地方修改了 model ,那么你就需要通过手动调用 $apply() 来通知 AngularJS 。这就像告诉 AngularJS ,你修改了一些 models ,希望 AngularJS 帮你触发 watchers 来做出正确的响应。

我们到底什么时候需要去调用apply()方法呢?情况非常少,实际上几乎我们所有的代码都包在scope.apply()里面,像ng−click,controller的初始化,http的回调函数等。在这些情况下,我们不需要自己调用,实际上我们也不能自己调用,否则在apply()方法里面再调用apply()方法会抛出错误。如果我们需要在一个新的执行序列中运行代码时才真正需要用到它,而且当且仅当这个新的执行序列不是被angular JS的库的方法创建的,这个时候我们需要将代码用scope.apply()包起来。下面用一个例子解释:

<!DOCTYPE html>
<html lang="en" ng-app="myapp">
<head>
    <meta charset="UTF-8">
    <title>$apply</title>
    <script src="js/angular.js"></script>
</head>
<body>
<div ng-controller="ServiceController">
{{message}}
</div>
<script>
    var app = angular.module('myapp', []);
    app.controller('ServiceController', function($scope) {
        $scope.message ="Waiting 2000ms for update";
        setTimeout(function () {
            $scope.message ="Timeout called!";
        }, 2000);
    });
</script>
</body>
</html>

上面的代码执行后页面上会显示:Waiting 2000ms for update。显然数据的更新没有被angular JS觉察到。
     接下来,我们将Javascript的代码稍作修改,用 scope.apply()包起来。

<!DOCTYPE html>
<html lang="en" ng-app="myapp">
<head>
    <meta charset="UTF-8">
    <title>$apply</title>
    <script src="js/angular.js"></script>
</head>
<body>
<div ng-controller="ServiceController">
{{message}}
</div>
<script>
    var app = angular.module('myapp', []);
    app.controller('ServiceController', function($scope) {
        $scope.message ="Waiting 2000ms for update";
        setTimeout(function () {
            $scope.$apply(function () {
                $scope.message ="Timeout called!";
            });
        }, 2000);
    });
</script>
</body>
</html>

   这次与之前不同的是,页面上先会显示:Waiting 2000ms for update,等待2秒后内容会被更改为:Timeout called! 。显然数据的更新被angular JS觉察到了。
     NOTE:我们不应该这样做,而是用angular JS提供的timeout方法,这样它就会被自动用apply方法包起来了

如果你运行了上面的例子,你会看到 view 在两秒钟之后也会更新。唯一的变化是我们的代码现在被 wrapped 到了 $scope.$apply() 中,它会自动触发 $rootScope.$digest() ,从而让 watchers 被触发用以更新 view

Note: 顺便提一下,你应该使用 $timeout service 来代替 setTimeout() ,因为前者会帮你调用 $apply() ,让你不需要手动地调用它。

总结:

需要记住的最重要的是 AngularJS 是否能检测到你对于 model 的修改。如果它不能检测到,那么你就需要手动地调用 $apply()


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值