重点的东西放上面,说三遍:

记住的最重要的是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()讲解的更多相关文章

  1. angular $apply()以及$digest()讲解1

    一些知名的批评和缺陷.他们都涉及到$digest loop(更新周期)中一个很常见的问题:如何在Angular之外更新$scope? 在哪调用 $apply? 更佳的做法是确保你是在$digest l ...

  2. $apply()和$digest()——angular

    $apply()和$digest()在AngularJS中是两个核心概念,但是有时候它们又让人困惑.而为了了解AngularJS的工作方式,首先需要了解$apply()和$digest()是如何工作的 ...

  3. 理解Angular中的$apply()以及$digest()

    $apply()和$digest()在AngularJS中是两个核心概念,但是有时候它们又让人困惑.而为了了解AngularJS的工作方式,首先需要了解$apply()和$digest()是如何工作的 ...

  4. 深入理解Angular中的$Apply()以及$Digest()

    $apply()和$digest()在AngularJS中是两个核心概念,但是有时候它们又让人困惑.而为了了解AngularJS的工作方式,首先需要了解$apply()和$digest()是如何工作的 ...

  5. 通俗理解angularjs中的$apply,$digest,$watch

    <!DOCTYPE html> <html lang="zh-CN" ng-app="app"> <head> <me ...

  6. --@angularjs--理解Angular中的$apply()以及$digest()

    $apply() 和 $digest() 在 AngularJS 中是两个核心概念,但是有时候它们又让人困惑.而为了了解 AngularJS 的工作方式,首先需要了解 $apply() 和 $dige ...

  7. (网页)理解Angular中的$apply()以及$digest()

    转自CSDN: 工作有问题上CSDN上转转. $apply()和$digest()在AngularJS中是两个核心概念,但是有时候它们又让人困惑.而为了了解AngularJS的工作方式,首先需要了解$ ...

  8. (转) 理解Angular中的$apply()以及$digest()

    原文地址:http://blog.csdn.net/dm_vincent/article/details/38705099 $apply()和$digest()在AngularJS中是两个核心概念,但 ...

  9. 理解$watch ,$apply 和 $digest --- 理解数据绑定过程

    原文地址:http://angular-tips.com/blog/2013/08/watch-how-the-apply-runs-a-digest/ 注 这篇博文主要是写给新手的,是给那些刚刚开始 ...

随机推荐

  1. android的照片浏览器(一)至返回所有图片文件

    今天开始写android的照片浏览器 首先要解决的问题是要得到sdcard下面所有是图片的文件的目录 于是我先写了一个普通的java类 来得到后缀是.jpg,.bmp.png.jpeg的文件 pack ...

  2. JavaScript笔记之数组 keyword(存储和释放&堆栈 & 按值 引用)

    1.数组创建及初始化 var obj=new Array(); var arr=[]; 可以延伸为长度一定的,字面量定义数组 2.堆栈 按值传递 引用类型 数组是引用类型,不是值传递, 栈:系桶自动分 ...

  3. ajax实现的无刷新分页代码实例

    一.html代码部分: <table class="table style-5">   <thead id="t_head">     ...

  4. [Java] 匿名内部类

    package test.file; import java.io.File; import java.io.FilenameFilter; /** * 匿名的内部类 * @author Frost. ...

  5. [ActionScript&Flex] FlashBuilder编译条件之如何屏蔽调试代码

    下面讲一下在FlashBuilder中如何添加编译器参数使我们在发布的时候不编译调试代码: 首先设置编译参数 编译参数设置好后,代码我们可以这样写: public class ConditionalC ...

  6. HTTP权威指南一

    HTTP——因 特网的多媒体信使 每天, 都有数以亿万计的 JPEG 图片. HTML 页面. 文本文件. MPEG 电影. WAV音频文件. Java 小程序和其他资源在因 特网 上游弋. HTTP ...

  7. zabbix 修改输出web前端图片的日期格式

    zabbix并没有给定一个全局或者用户级别的时间格式定义方式. 实在看不惯的话,可以自己修改源代码来实现修改. 暂时研究了半小时,先把展示图片修改了. 后续有更严谨的方案,再更新此文吧. ------ ...

  8. 使用mustache.js 模板引擎输出html

    看了https://mustache.github.io/你就知道mustache是非常强大的模板引擎,支持多种语言,下面是个简单入门例子: MVC Model public class Studen ...

  9. (转)Java DES 与Base64

    原文地址http://blog.csdn.net/tomatozq/article/details/20773559 1,DES /** * 解密 * @param message * @param ...

  10. '@P0' 附近有语法错误

    问题出在ibatis中的某个orm配置文件,查看你的某些sql语句,尤其是用到#和$等进行赋值的,区分开到底是用#还是$ eg: select top $pagefrom$ id from tb_bo ...