angularJS中的$apply(),$digest(),$watch()

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

$apply()和$digest()

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) {  

});  

传入到$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循环。

目前为止还不错!但是,有一个小问题。在上面的例子中,AngularJS并不直接调用$digest(),而是调用$scope.$apply(),后者会调用$rootScope.$digest()。因此,一轮$digest循环在$rootScope开始,随后会访问到所有的children scope中的watchers。

现在,假设你将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来做出正确的响应。

比如,如果你使用了JavaScript中的setTimeout()来更新一个scope model,那么AngularJS就没有办法知道你更改了什么。这种情况下,调用$apply()就是你的责任了,通过调用它来触发一轮$digest循环。类似地,如果你有一个指令用来设置一个DOM事件listener并且在该listener中修改了一些models,那么你也需要通过手动调用$apply()来确保变更会被正确的反映到view中。

时间: 2024-10-12 21:02:08

angularJS中的$apply(),$digest(),$watch()的相关文章

angularJs中关于ng-class的三种使用方式说明

在开发中我们通常会遇到一种需求:一个元素在不同的状态需要展现不同的样子. 而在这所谓的样子当然就是改变其css的属性,而实现能动态的改变其属性值,必然只能是更换其class属性 这里有三种方法: 第一种:通过数据的双向绑定(不推荐) 第二种:通过对象数组 第三种:通过key/value 下面简单说下这三种: 第一种:通过数据的双向绑定 实现方式: function changeClass(){   $scope.className = "change2"; } <div clas

AngularJs中的directives(指令)

一.指令的职责   指令的职责是修改DOM结构,并将作用域和DOM连接起来.即指令既要操作DOM,将作用域内的数据绑定到DOM节点上,又要为DOM绑定事件调用作用域内的对应的方法. 二.创建自定义指令 调用自定义指令的4种方式:元素.属性.样式类.注释. 常用的是前两种,实例如下: <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title></title>

黄聪:AngularJS中的$resource使用与Restful资源交互(转)

原文:http://blog.csdn.net/he90227/article/details/50525836 1.AngularJS中的 $resource 这个服务可以创建一个资源对象,我们可以用它非常方便地同支持RESTful的服务端数据源进行交互,当同支持RESTful的数据模型一起工作时,它就派上用场了.      REST是Representational State Transfer(表征状态转移)的缩写,是服务器用来智能化地提供数据服务的一种方式 1)我们首先需要引入ng-Re

Spring Boot+AngularJS中因为跨域导致Session丢失

http://blog.csdn.net/dalangzhonghangxing/article/details/52446821 如果还在为跨域问题烦恼,请查看博主的 解决angular+spring boot的跨域问题 那篇文章. 博主在项目开发过程中,遇到了由于跨域而导致的Session丢失问题,非常的恶心,但是经过在网上查阅各种资料,发现解决方法十分简单. 在我们每一次的数据请求中,浏览器都会向后台发送一个JSession,后台会根据这个值自动查找Id为JSession的那个sessio

后端接收不到AngularJs中$http.post发送的数据的问题

1.问题: 后端接收不到AngularJs中$http.post发送的数据,总是显示为null 示例代码: $http.post(/admin/KeyValue/GetListByPage, { pageindex: 1, pagesize: 8 }) .success(function(){ alert("Mr靖"); }); 代码没有错,但是在后台却接收不到数据,这是为什么呢? 用火狐监控:参数是JSON格式 用谷歌监控:传参方式是request payload 可以发现传参方式是

Angularjs中的事件广播-浅谈$broadcast,$emit,$on

Angularjs中不同作用域之间可以通过组合使用$broadcast,$emit,$on的事件广播机制来进行通信 介绍: $broadcast的作用是将事件从父级作用域传播至子级作用域,包括自己.格式如下:$broadcast(eventName,args) $emit的作用是将事件从子级作用域传播至父级作用域,包括自己,直至根作用域.格式如下:$emit(eventName,args) $on用于在作用域中监控从子级或父级作用域中传播的事件以及相应的数据.格式如下:$on(event,dat

AngularJs中,如何在父元素中调用子元素为自定义Directive中定义的函数?

最近一段时间准备使用AngularJs中的自定义Directive重构一下代码. 在这里说明一下,把自定义控件封装成Directive并不一定是要复用,而是要让代码结构更加清晰.就好像你将一个长方法拆分成多个独立的小方法,也未必要复用它们一样.职责独立等一票好处,会让后期维护更加轻松. 在重构的过程中,我遇到了这样一个问题,先上图: 图一: 这就是我要重构的界面,由于之前时间紧,将这三个Filter和两个button都写在了一个页面中.当时我已经预感到,如果将这里面的状态都写到一个scope上,

Javacript和AngularJS中的Promises

promise是Javascript异步编程很好的解决方案.对于一个异步方法,执行一个回调函数. 比如页面调用google地图的api时就使用到了promise. function success(position){ var cords = position.coords; console.log(coords.latitude + coords.longitude); } function error(err){ console.warn(err.code+err.message) } na

AngularJS中实现Model缓存

在AngularJS中如何实现一个Model的缓存呢? 可以通过在Provider中返回一个构造函数,并在构造函数中设计一个缓存字段,在本篇末尾将引出这种做法. 一般来说,Model要赋值给Scope的某个变量. 有的直接把对象赋值给Scope变量:有的在Provider中返回一个对象再赋值给Scope变量:有的在Provider中返回一个构造函数再赋值给Scope变量.本篇来一一体验. 首先自定义一个directive,用来点击按钮改变一个scope变量值. angular .module('