zoukankan      html  css  js  c++  java
  • Angular之双向数据绑定(上)

    ---恢复内容开始---

    angular最初进入前端开发人员视野的时候,给人以不可磨灭的印象之一就是它的双向数据绑定的实现。本篇章会先介绍如何使用此功能,然后在深入解释它的双向绑定的机制是如何实现的。

    angular中的data-binding指的是模型models和视图views之间的自动同步。angular实现双向绑定后,会让你觉得数据模型是页面中数据唯一的真实来源。当model改变后,视图反映改变,反之亦然。通俗的说,所谓的双向数据绑定,无非就是从界面的操作能实时反映到数据,数据的变更能实时展现到界面。据各最简单的例子:

    <div ng-controller="CounterCtrl">
        <span ng-bind="counter"></span>
        <button ng-click="counter++">increase</button>
    </div>
    function CounterCtrl($scope) {
        $scope.counter = 1;
    }

    上面的例子很简单,每当点击一次按钮,界面上的数据就加1。

    但是,新手很可能会碰到下面这样的问题。

    var app = angular.module("test", []);
    
    app.directive("myclick", function() {
        return function (scope, element, attr) {
            element.on("click", function() {
                scope.counter++;
            });
        };
    });
    
    app.controller("CounterCtrl", function($scope) {
        $scope.counter = 0;
    });
    
    <body ng-app="test">
        <div ng-controller="CounterCtrl">
            <button myclick>increase</button>
            <span ng-bind="counter"></span>
        </div>
    </body>

    上面的例子也很简单:想要实现的事:点击按钮时,span元素中counter加1。但是实际上,视图上并不会这样。然而model中counter确实增加了。也就是说,并没有实现angular所说的数据双向绑定。测试请访问:http://plnkr.co/edit/?p=preview

    但是如果在scope.counter++;后面加上scope.$digest();后又没问题了,而第一个例子中并没有使用$digest函数,如果使用了反而报错。这是怎么回事?不妨先看看下面怎么使用原生javascript来实现的。

    <!DOCTYPE html>
    <html>
        <head>
            <meta charset="utf-8" />
            <title>two-way binding</title>
        </head>
        <body onload="init()">
            <button ng-click="inc">
                increase 1
            </button>
            <button ng-click="inc2">
                increase 2
            </button>
            <span style="color:red" ng-bind="counter"></span>
            <span style="color:blue" ng-bind="counter"></span>
            <span style="color:green" ng-bind="counter"></span>
    
            <script type="text/javascript">
                /* 数据模型区开始 */
                var counter = 0;
    
                function inc() {
                    counter++;
                }
    
                function inc2() {
                    counter+=2;
                }
                /* 数据模型区结束 */
    
                /* 绑定关系区开始 */
                function init() {
                    bind();
                }
    
                function bind() {
                    var list = document.querySelectorAll("[ng-click]");
                    for (var i=0; i<list.length; i++) {
                        list[i].onclick = (function(index) {
                            return function() {
                                window[list[index].getAttribute("ng-click")]();
                                apply();
                            };
                        })(i);
                    }
                }
    
                function apply() {
                    var list = document.querySelectorAll("[ng-bind='counter']");
                    for (var i=0; i<list.length; i++) {
                        list[i].innerHTML = counter;
                    }
                }
                /* 绑定关系区结束 */
            </script>
        </body>
    </html>

    上面绑定model和view时,bind函数通过监听'ng-click'属性的元素队列,再调用apply函数把对应的counter写入到该元素中。

    上面没有直接使用DOM的onclick方法,而是搞了一个ng-click,然后在bind里面把这个ng-click对应的函数拿出来,绑定到onclick的事件处理函数中。为什么要这样呢?因为数据虽然变更了,但是还没有往界面上填充,我们需要在此添加一些附加操作,即添加apply()方法。

    而由于angular使用的是脏检测,也就是说,需要自己做一些事情来触发脏检测,在应用到这个数据对应的DOM元素上。所以前面一段代码只是监听了原始click事件,而不是‘ng-click’对应的事件,并没有触发脏检查,不会更新到视图上面。(不清楚脏检查的同学,可以先跳到下一篇弄明白。)

    在一些基于setter的框架中,它可以在给数据设置的时候,对DOM元素上绑定的变量重新设值。但是脏检查的机制并没有这个阶段,它没有任何途径在数据变更之后,立即得到通知,所以只能在每个事件入口手动调用apply(),把数据模型的改变反映到视图上。在真正的angular中,一般都是先对模型数据进行脏检查,确实改变了,才对视图设值。所以我们在ng-click中封装click事件,母的是为了在click事件后追加apply()方法,这样才能把model data绑定到view上。

    那么,为什么在ng-click里面调用$digest的话,会报错呢(就是第一段代码)?因为Angular的设计,同一时间只允许一个$digest运行,而ng-click这种内置指令已经触发了$digest,当前的还没有走完,所以就出错了。

    上面的问题归结为:angualr怎么触发脏检查?什么时候触发?那就不得不提到scope下面的三个重要方法:$digest,$apply,$watch.下篇详细介绍这三个函数,包括脏检查的机制。 

  • 相关阅读:
    form.submit 方法 并不会触发 form.onsubmit 事件
    火狐 a 标签 download 属性,要在 a 标签添加到页面中才生效;
    git、git bash、git shell的区别
    maven安装本地jar包
    JFinal极速开发框架使用笔记(四) _JFinalDemoGenerator实体类生成及映射自动化
    JFinal极速开发框架使用笔记(三) 分析Model和ActiveRecord
    JFinal极速开发框架使用笔记(二) 两个问题,一个发现
    Java常用工具类之Excel导出
    Java常用工具类之删除文件
    Java常用工具类之自定义访问对象
  • 原文地址:https://www.cnblogs.com/brancepeng/p/5002582.html
Copyright © 2011-2022 走看看