zoukankan      html  css  js  c++  java
  • AngularJS 的数据绑定

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

    转:http://huangtengfei.com/2015/09/data-bind-of-angularjs/

  • 相关阅读:
    如何分秒必争--浅淡时间切片器
    2019微软Power BI 每月功能更新系列——Power BI 6月版本功能完整解读
    2019微软Power BI 每月功能更新系列——Power BI 5月版本功能完整解读
    自制操作系统 ---- 资料
    [书籍推荐] 编程入门学习必备
    《30天自制操作系统》笔记5 --- (Day3)进入32位模式并导入C语言
    精彩知识视频分享
    《30天自制操作系统》笔记4 --- (Day2 下节)了解如何用汇编写操作系统中的HelloWorld
    《30天自制操作系统》笔记3 --- (Day2 上节)完全解析文件系统
    新版Notepad++加十六进制查看的插件HexEditor
  • 原文地址:https://www.cnblogs.com/ilinuxer/p/5243528.html
Copyright © 2011-2022 走看看