关于 AngularJS 的数据绑定
2016-03-04 00:02
796 查看
单向绑定(ng-bind) 和 双向绑定(ng-model) 的区别
ng-bind单向数据绑定(
$scope -> view),用于数据显示,简写形式是
{{}}。
1 | <span ng-bind="val"></span> |
{{val}}会直接显示到页面,直到 Angular 渲染该绑定数据(这种行为有可能将
{{val}}让用户看到);而
ng-bind则是在 Angular 渲染完毕后将数据显示。
ng-model是双向数据绑定(
$scope -> viewand
view -> $scope),用于绑定值会变化的表单元素等。
1 | <input type="text" ng-model="val" /> |
双向数据绑定的原理
双向数据绑定意味着当view中有任何数据发生变化会自动地反馈到
scope的数据上,当
scope模型发生变化时,
view中的数据也会更新到最新的值。很显然,这需要一个监控。
事实上,AngularJS 确实在幕后为
scope模型上设置了一个 监听队列,用来监听数据变化并更新
view。
每次绑定一个东西到
view上时 AngularJS 就会往
$watch队列里插入一条
$watch,用来检测它监视的
model里是否有变化的东西。
当浏览器接收到可以被
angular context处理的事件时,
$digest循环就会触发。
$digest会遍历所有的
$watch。
一次更新的操作(至少触发两次 $digest() 循环)
比如进行一次click操作:
1 | <button ng-click="val=val+1">increase 1</button> |
浏览器接收到一个事件,进入
angular context
$digest循环开始执行,查询每个
$watch是否变化
由于监视
$scope.val的
$watch报告了变化,它会强制再执行一次
$digest循环。
新的
$digest循环没有检测到变化。
浏览器拿回控制权,更新与
$scope.val新值相应部分的
DOM。
$digest 循环会运行多少次?
$digest循环不会只运行一次。在当前的一次循环结束后,它会再执行一次循环用来检查是否有
models发生了变化。
这就是脏检查(
Dirty Checking),它用来处理在
listener函数被执行时可能引起的
model变化。因此
$digest循环会持续运行直到
model不再发生变化,或者
$digest循环的次数达到了
10次(超过
10次后抛出一个异常,防止无限循环)。
当
$digest循环结束时,
DOM相应地变化。
$apply() 和 $digest() 的区别
$apply是
$scope(或者是
direcvie里的
link函数中的
scope)的一个函数,调用它会强制一次
$digest循环(除非当前正在执行循环,这种情况下会抛出一个异常,这是我们不需要在那里执行
$apply的标志)。
$apply()和
$digest()有两个区别。
1) 最直接的差异是,
$apply可以带参数,它可以接受一个函数,然后在应用数据之后,调用这个函数。所以,一般在集成非 Angular 框架(比如jQuery)的代码时,可以把代码写在这个里面调用。
2) 当调用
$digest的时候,只触发当前作用域和它的子作用域上的监控,但是当调用
$apply的时候,会触发作用域树上的所有监控。
什么时候手动调用 $apply() 方法?
取决于是否在 Angular 上下文环境(angular context)。
典型的需要调用
$apply()方法的场景是:
1) 使用了 JavaScript 中的
setTimeout()来更新一个
scope model
2) 用指令设置一个
DOM事件
listener并且在该
listener中修改了一些
models
场景一
12 | $scope.setMsg = function() { setTimeout(function() { $scope.message = 'hello world'; console.log('message:' + $scope.message); }, 2000); } $scope.setMsg(); |
model,然而,
view并没有更新。
在
$scope.getMessage加入
$apply()方法。
12 | $scope.getMessage = function() { setTimeout(function() { $scope.$apply(function() { $scope.message = 'hello world'; console.log('message:' + $scope.message); }); }, 2000); } |
不过,在 AngularJS 中应该尽量使用
$timeoutService 来代替
setTimeout(),因为前者会帮你调用
$apply(),让你不需要手动地调用它。
12 | $timeout(function(){ $scope.message = 'hello world'; console.log('message:' + $scope.message); }, 2000) |
场景二
实现一个click的指令,类似以下功能:
1 | <button ng-click="val=val+1">increase 1</button> |
directive的编写如下:
12 | app.directive("inc", function() { return function (scope, element, attr) { element.on("click", function() { scope.val++; }); }; }); |
在
scope.val++;一行后面添加
scope.$apply();或者
scope.$digest();就 OK 了。
$apply() 方法的两种形式
1) 无参1 | $scope.$apply() |
12 | $scope.$apply(function(){ ... }) |
function作为参数的
$apply()方法。这是因为当传入一个
function到
$apply()中的时候,这个
function会被包装到一个
try…catch块中,所以一旦有异常发生,该异常会被
$exceptionHandler service处理。
想象一下如果有个
alert框显示错误给用户,然后有个第三方的库进行一个网络调用然后失败了,如果不把它封装进
$apply里面,Angular 永远不会知道失败了,
alert框就永远不会弹出来了。
在 AngularJS 中使用 $watch
常用的使用方式:12 | $scope.name = 'htf'; $scope.$watch('name', function(newValue, oldValue) { if (newValue === oldValue) { return; } $scope.updated++; }); |
$watch()中的第二个参数是一个回调函数,该函数在
name的值发生变化的时候会被调用。
如果要监听的是一个对象,那还需要第三个参数:
12 | $scope.data.name = 'htf'; $scope.$watch('data', function(newValue, oldValue) { if (newValue === oldValue) { return; } $scope.updated++; }, true); |
true,在
data.name变化时,不会触发相应操作,因为引用的是同一引用。
参考
理解$watch ,$apply 和 $digest —- 理解数据绑定过程理解Angular中的$apply()以及$digest()
Angular沉思录(一)数据绑定
构建自己的AngularJS,第一部分:Scope和Digest
相关文章推荐
- 【Angularjs文档翻译及实例】DOM事件
- AngularJS 常用模块书写建议
- AngularJS中的依赖注入
- 『AngularJS』$location 服务介绍(1)
- angularjs用FormData上传文件
- angularjs表达式中不能直接调用外部的js函数
- AngularJS中的控制器和作用域
- angularjs 和 trim方法冲突
- #学习笔记#(51)angular自定义标签director
- 走进AngularJs(八) ng的路由机制
- Angularjs Ng_repeat中实现复选框选中并显示不同的样式
- 走进AngularJs(中) 服务机制1
- ng-include用法分析以及多标签页面的简单实现方式
- Angularjs轻松实现表格按指定列排序
- 【Angularjs文档翻译及实例】表单指令集(input)
- AngularJs学习记录
- angularjs(1)
- #学习笔记#(50)angular.bind(self,fn,args)
- Angularjs表达式与JavaScript表达式的区别
- bootstrap + angularjs + springmvc + mybatis框架之多个输入框(可为空)组合查询