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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM