angular $apply()以及$digest()讲解
重点的东西放上面,说三遍:
记住的最重要的是ng是否能检测到你对于model的修改。如果它不能检测到,那么你就需要手动地调用$apply()!
记住的最重要的是ng是否能检测到你对于model的修改。如果它不能检测到,那么你就需要手动地调用$apply()。
记住的最重要的是ng是否能检测到你对于model的修改。如果它不能检测到,那么你就需要手动地调用$apply()。
$apply()和$digest()在AngularJS中是两个核心概念。为了了解AngularJS的工作方式,首先需要了解$apply()和$digest()是如何工作的。
ng中提供了一个很酷的特性叫双向数据绑定(two-way Data Binding),当view和scope中的数据发生变化时会相互自动更新。
ng是如何做到的呢?下面来讲解一下:
当写下表达式{{model}}时,ng在幕后偷偷为你做一件事:
在scope模型上设置一个watcher,用来监听数据发生变化的时候更新view。ng会周期性的运行一个函数来检查scope模型数据是否发生变化。
这时候,$digest就出来发力了。
watcher原理:
$scope.$watch('model', function(newValue, oldValue) {
//update the DOM with newValue
});
在$digest循环中,watchers会被触发,当一个watcher被触发时,ng会检测scope模型,如何检测到发生变化,关联到watcher的回调函数就会被调用。
下面问题来了,$digest循环什么时候以什么方式开始呢?
在调用了$scope.$digest()后,$digest循环就开始了。举例:
你在一个ng-click指令对应的handler函数中更改了scope中的一条数据,此时AngularJS会自动地通过调用$digest()来触发一轮$digest循环。当$digest循环开始后,它会触发每个watcher。这些watchers会检查scope中的当前model值是否和上一次计算得到的model值不同。如果不同,那么对应的回调函数会被执行。调用该函数的结果,就是view中的表达式内容(译注:诸如{{ model }})会被更新。
OK,还有个小注意事项:》》》》》》ng并不会直接调用$digest()。而是调用$scope.$apply(),然后调用$rootScope.$digest()。So,一轮$digest循环在$rootScope开始,随后会访问到所有的children scope中的watchers。
现在,我们假设button上有个方法fn1:
<button ng-click="fn1()"></button>
当点击时,ng会将fn1包装到wrappering function中,然后传入到$scope.$apply()。因此我的fn1也会被正常执行。
如果点击后数据model数据发生变化,新一轮的$digest loop循环也会被触发。
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来做出正确的响应。
比如,如果你使用了JavaScript中的setTimeout()来更新一个scope model,那么AngularJS就没有办法知道你更改了什么。这种情况下,调用$apply()就是你的责任了,通过调用它来触发一轮$digest循环。类似地,如果你有一个指令用来设置一个DOM事件listener并且在该listener中修改了一些models,那么你也需要通过手动调用$apply()来确保变更会被正确的反映到view中。
来看一个例子:有一个页面,一旦该页面加载完毕了,你希望在两秒钟之后显示一条信息。

你会看到过了2秒钟之后,控制台确实会显示出已经更新的model,然而,view并没有更新。
调用$scope.$apply,修改如下:

运行了上面的例子,你会看到view在两秒钟之后也会更新。
唯一的变化是我们的代码现在被wrapped到了$scope.$apply()中,它会自动触发$rootScope.$digest(),从而让watchers被触发用以更新view。
tips: 顺便提一下,你应该使用$timeout service来代替setTimeout(),因为前者会帮你调用$apply(),让你不需要手动地调用它。
$apply()的第二种形式(没有参数):
以上代码也可以在你修改model之后手动调用没有参数的$apply(),也能实现让view更新:
$scope.getMessage = function() {
setTimeout(function() {
$scope.message = 'Fetched after two seconds';
console.log('message:' + $scope.message);
$scope.$apply(); //this triggers a $digest
}, );
};
需要记住的是你总是应该使用接受一个function作为参数的$apply()方法。这是因为当你传入一个function到$apply()中的时候,这个function会被包装到一个try…catch块中,所以一旦有异常发生,该异常会被$exceptionHandler service处理。
$digest循环会运行多少次?
当一个$digest循环运行时,watchers会被执行来检查scope中的models是否发生了变化。如果发生了变化,那么相应的listener函数就会被执行。这涉及到一个重要的问题。如果listener函数本身会修改一个scope model呢?AngularJS会怎么处理这种情况?
答案是$digest循环不会只运行一次。在当前的一次循环结束后,它会再执行一次循环用来检查是否有models发生了变化。这就是脏检查(Dirty Checking),它用来处理在listener函数被执行时可能引起的model变化。因此,$digest循环会持续运行直到model不再发生变化,或者$digest循环的次数达到了10次。因此,尽可能地不要在listener函数中修改model。
Note: $digest循环最少也会运行两次,即使在listener函数中并没有改变任何model。正如上面讨论的那样,它会多运行一次来确保models没有变化。
结语
记住的最重要的是ng是否能检测到你对于model的修改。如果它不能检测到,那么你就需要手动地调用$apply()。
angular $apply()以及$digest()讲解的更多相关文章
- angular $apply()以及$digest()讲解1
一些知名的批评和缺陷.他们都涉及到$digest loop(更新周期)中一个很常见的问题:如何在Angular之外更新$scope? 在哪调用 $apply? 更佳的做法是确保你是在$digest l ...
- $apply()和$digest()——angular
$apply()和$digest()在AngularJS中是两个核心概念,但是有时候它们又让人困惑.而为了了解AngularJS的工作方式,首先需要了解$apply()和$digest()是如何工作的 ...
- 理解Angular中的$apply()以及$digest()
$apply()和$digest()在AngularJS中是两个核心概念,但是有时候它们又让人困惑.而为了了解AngularJS的工作方式,首先需要了解$apply()和$digest()是如何工作的 ...
- 深入理解Angular中的$Apply()以及$Digest()
$apply()和$digest()在AngularJS中是两个核心概念,但是有时候它们又让人困惑.而为了了解AngularJS的工作方式,首先需要了解$apply()和$digest()是如何工作的 ...
- 通俗理解angularjs中的$apply,$digest,$watch
<!DOCTYPE html> <html lang="zh-CN" ng-app="app"> <head> <me ...
- --@angularjs--理解Angular中的$apply()以及$digest()
$apply() 和 $digest() 在 AngularJS 中是两个核心概念,但是有时候它们又让人困惑.而为了了解 AngularJS 的工作方式,首先需要了解 $apply() 和 $dige ...
- (网页)理解Angular中的$apply()以及$digest()
转自CSDN: 工作有问题上CSDN上转转. $apply()和$digest()在AngularJS中是两个核心概念,但是有时候它们又让人困惑.而为了了解AngularJS的工作方式,首先需要了解$ ...
- (转) 理解Angular中的$apply()以及$digest()
原文地址:http://blog.csdn.net/dm_vincent/article/details/38705099 $apply()和$digest()在AngularJS中是两个核心概念,但 ...
- 理解$watch ,$apply 和 $digest --- 理解数据绑定过程
原文地址:http://angular-tips.com/blog/2013/08/watch-how-the-apply-runs-a-digest/ 注 这篇博文主要是写给新手的,是给那些刚刚开始 ...
随机推荐
- POJ 3311 Hie with the Pie(Floyd+状态压缩DP)
题是看了这位的博客之后理解的,只不过我是又加了点简单的注释. 链接:http://blog.csdn.net/chinaczy/article/details/5890768 我还加了一些注释代码,对 ...
- memcpy
函数原型 void *memcpy(void*dest, const void *src, size_t n); 功能 由src指向地址为起始地址的连续n个字节的数据复制到以destin指向地址为起始 ...
- 关于 MySQL UTF8 编码下生僻字符插入失败/假死问题的分析
原文:http://my.oschina.net/leejun2005/blog/343353 目录[-] 1.问题:mysql 遇到某些中文插入异常 2.原因:此 utf8 非彼 utf8 3.解决 ...
- iOS8 StoryBoard 连线diss方法
添加自定义Dismiss类: // Dismiss.h // StoryBoardTest // // Created by zhujin on 14/12/23. // Copyright ...
- doc2vec 利用gensim 生成文档向量
利用gensim 直接生成文档向量 def gen_d2v_corpus(self, lines): with open("./data/ques2_result.txt", &q ...
- 使用python + tornado 做项目demo演示模板
很简单,可是却也折腾了不是时间,走了不少弯路.在此备注记录一下,以供后需. # web_server.py #!/usr/bin/env python # coding=utf-8 import os ...
- expdp和impdp的用法
源地址:http://blog.chinaunix.net/uid-23622436-id-2394094.html
- 页面设计--Grid列表
Grid列表控件 功能:主要实现Html Table功能,支持多表头.固定表头.固定列.输出.组合查询.编辑功能 优点:可以快速的通过数据集合生成多表头.多样式的列表.通过简单的拖拉实现多层表头. G ...
- unix环境C编程之日期时间转换
1.理清概念 1.1.日历时间: 含义:国际标准时间1970年1月1日00:00:00以来经过的秒数. 数据类型:time_t.实际上是long的别名. 1.2.tm结构时间: 含义:结构 ...
- 《Code Complete》ch.22 开发者测试
WHAT? 单元测试(Unit Testing):是将一个程序员或一个开发团队所编写的,一个完整的类.子程序或者小程序,从完整的系统中隔离出来进行测试 组件测试(Component Testing): ...