单向绑定(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

场景一

1
2
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() 方法。

1
2
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(),让你不需要手动地调用它。

1
2
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 的编写如下:

1
2
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) 有参

1
2
3
$scope.$apply(function(){
...
})

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

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

在 AngularJS 中使用 $watch

常用的使用方式:

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

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

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

1
2
3
4
5
$scope.data.name = 'htf';
$scope.$watch('data', function(newValue, oldValue) {
if (newValue === oldValue) { return; }
$scope.updated++;
}, true);

表示比较的是对象的值而不是引用,如果不加第三个参数 true ,在 data.name 变化时,不会触发相应操作,因为引用的是同一引用。

参考

  1. 理解$watch ,$apply 和 $digest —- 理解数据绑定过程
  2. 理解Angular中的$apply()以及$digest()
  3. Angular沉思录(一)数据绑定
  4. 构建自己的AngularJS,第一部分:Scope和Digest

关于 AngularJS 的数据绑定的更多相关文章

  1. 10分钟学会AngularJS的数据绑定

     前言:为什么要用AngularJS?  相信用过.NetMVC的人都知道用rezor绑定数据是一件很爽的事情,C#代码直接在前台页面中输出.然后这种比较适用于同步请求.   当我们的项目离不开异步请 ...

  2. AngularJS双向数据绑定

    <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title> ...

  3. 02、AngularJs的数据绑定

    我们知道,AngularJs中的数据绑定是双向绑定的,View的改变,会改变Model,Model的改变也会改变View中的值,废话不多说,我们直接上代码. <!DOCTYPE html> ...

  4. AngularJS笔记---数据绑定

    一.数据绑定 1.简单绑定 下面实现了一个简单的加法运算的绑定, A.ng-app:表示该div以内都在AngularJS的应用, 去掉ng-app="" 那么后面的绑定都将无效 ...

  5. AngularJS 的数据绑定

    单向绑定(ng-bind) 和 双向绑定(ng-model) 的区别 ng-bind 单向数据绑定($scope -> view),用于数据显示,简写形式是 {{}}. 1 <span n ...

  6. Vue、AngularJS 双向数据绑定解剖

    数据与视图的绑定与同步,最终体现在对数据的读写处理过程中,也就是 Object.defineProperty() 定义的数据 set.get 函数中.Vue 中对于的函数为 defineReactiv ...

  7. 关于AngularJs,数据绑定与自定义验证

    最近开始着手学起了Angular,抱着好奇的心情开始研究了起来.忽然发现angular可以巧妙而方便的进行数据的绑定验证啊什么的.(当然,我只是刚开始学,所有可能有更强大的功能,只是我还没有看到) 那 ...

  8. AngularJS入门心得2——何为双向数据绑定

    前言:谁说Test工作比较轻松,最近在熟悉几个case,差点没疯.最近又是断断续续的看我的AngularJS,总觉得自己还是没有入门,可能是自己欠前端的东西太多了,看不了几行代码就有几个常用函数不熟悉 ...

  9. AngularJS语法基础及数据绑定——详解各种数据绑定指令、属性应用

    AngularJS简单易学,但是功能强大.特别是在构建单页面应用方面效果显著.而 数据绑定 可以说是他被广泛使用的最主要的优点.他舍弃了对DOM的操作方式,一切都由AngularJS来自动更新视图,我 ...

随机推荐

  1. python使用游标访问数据

    游标是一种数据访问对象,可用于在表中迭代一组行或者向表中插入新行.游标有三种形式:搜索.插入或更新.游标通常用于读取现有几何和写入新几何. 每种类型的游标均由对应的 ArcPy 函数(SearchCu ...

  2. eclipse安装Run-Jetty-Run插件,修改实时生效

    http://marketplace.eclipse.org/content/run-jetty-run   1.直接拖拽到eclipse安装(7/8/9版本都安装) 2.以调试的方式启动jetty( ...

  3. Eclipse NDK 配置,不用安装Cygwin

    一.关于NDK:NDK全称:Native Development Kit.1.NDK是一系列工具的集合.NDK提供了一系列的工具,帮助开发者快速开发C(或C++)的动态库,并能自动将so和java应用 ...

  4. linux ntfs模块

    步骤: 1.在/usr/src/linux-2.4.18-3/configs/目录下 找适合自己机器的内核配置文件.我用的kernel-2.4.18-x86_64.config,把它拷贝到/usr/s ...

  5. 最大的Redis集群:新浪Redis集群揭秘

    前言 Tape is Dead,Disk is Tape,Flash is Disk,RAM Locality is King.       — Jim Gray Redis不是比较成熟的Memcac ...

  6. BP反向传播算法的工作原理How the backpropagation algorithm works

    In the last chapter we saw how neural networks can learn their weights and biases using the gradient ...

  7. 8.volatile原子性

    原子性     1.一个操作是不可中断的,即使多个线程在一起执行的时候,一旦操作执行开始,就不会被其他的线程干扰执行并导致执行中断.     2.对于静态变量int ,2个线程同时对它进行修改,线程a ...

  8. log4net 自定义Appender

    最近有个需求,使用log4net来记录日志,然后将数据保存到服务器端.一开始打算写一个windows service,定期上传日志. 后来又因为一些场景下不适应,因此直接改为保存内存中,到一定阀值之后 ...

  9. JNI字段描述符

    “([Ljava/lang/String;)V” 它是一种对函数返回值和参数的编码.这种编码叫做JNI字段描述符(JavaNative Interface FieldDescriptors).一个数组 ...

  10. Openwrt WIFI探针开发【一】

    2017.9.26 公开源码(Apache2.0协议) https://github.com/769484623/WiFiProbe ————————————————————————————————— ...