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.下篇詳細介紹這三個函數,包括髒檢查的機制。

相關文章
相關標籤/搜索