Angular之双向数据绑定(上)

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

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

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。java

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

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

而因为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.下篇详细介绍这三个函数,包括脏检查的机制。

相关文章
相关标签/搜索