您的位置:首页 > Web前端 > AngularJS

关于 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 -> view
and
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
3
4
5
6
7

$scope.setMsg = function() {
setTimeout(function() {
$scope.message = 'hello world';
console.log('message:' + $scope.message);
}, 2000);
}
$scope.setMsg();

运行这个例子,会看到过了两秒钟之后,控制台确实会显示出已经更新的
model
,然而,
view
并没有更新。

$scope.getMessage
加入
$apply()
方法。

12
3
4
5
6
7
8

$scope.getMessage = function() {
setTimeout(function() {
$scope.$apply(function() {
$scope.message = 'hello world';
console.log('message:' + $scope.message);
});
}, 2000);
}

再运行就 OK 了。

不过,在 AngularJS 中应该尽量使用
$timeout
Service 来代替
setTimeout()
,因为前者会帮你调用
$apply()
,让你不需要手动地调用它。

12
3
4

$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
3
4
5
6
7

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

2) 有参

12
3

$scope.$apply(function(){
...
})

应该总使用接受一个
function
作为参数的
$apply()
方法。这是因为当传入一个
function
$apply()
中的时候,这个
function
会被包装到一个
try…catch
块中,所以一旦有异常发生,该异常会被
$exceptionHandler service
处理。

想象一下如果有个
alert
框显示错误给用户,然后有个第三方的库进行一个网络调用然后失败了,如果不把它封装进
$apply
里面,Angular 永远不会知道失败了,
alert
框就永远不会弹出来了。

在 AngularJS 中使用 $watch

常用的使用方式:

12
3
4
5

$scope.name = 'htf';
$scope.$watch('name', function(newValue, oldValue) {
if (newValue === oldValue) { return; }
$scope.updated++;
});

传入到
$watch()
中的第二个参数是一个回调函数,该函数在
name
的值发生变化的时候会被调用。

如果要监听的是一个对象,那还需要第三个参数:

12
3
4
5

$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
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: