【摘要】 Angularjs1.X中兩種不一樣的雙向數據綁定聊聊 Angularjs1.x中那些活見鬼的事情。一. html與Controller中的雙向數據綁定html-Controller的雙向數據綁定,在開發中很是常見,也是Angularjs1.x的宣傳點之一,使用中並無太多問題。javascript
聊聊 Angularjs1.x中那些活見鬼的事情。html
html-Controller的雙向數據綁定,在開發中很是常見,也是Angularjs1.x的宣傳點之一,使用中並無太多問題。java
1.1數據從html流向controller安全
也就是從視圖層流向模型層,原生html中須要使用表單元素(例如input
標籤)來收集用戶輸入信息,Angularjs中經過在表單元素上使用ng-model
標籤,當用戶輸入信息時,同步將用戶輸入的信息賦值給controller中的變量:app
<body ng-app="myApp"> <div id="main" ng-controller="myCtrl"> <p>改變輸出值:</p> <input type="text" ng-model="testInfo.content" ng-change="showInput()"> </div> <script src="./angular.min.js"></script> <script> angular.module('myApp',[]) .controller('myCtrl',['$scope',function($scope){ $scope.showInput = function() { console.log($scope.testInfo.content); } }]); </script></body>
在頁面上輸入1234567便可看到,每次在頁面輸入數字後,控制檯輸出的$scope,testInfo.content
的值都和頁面保持一致:ide
1.2 數據從controller流向html函數
也就是從模型層流向數據層,當controller中的數據模型變量發生變化後,Angularjs又會根據數據模型的值去改變ng-model
指令綁定的表單元素的值,使用ng-bind
指令也能夠被動得到來自controller的數據流。測試
咱們編寫以下demo進行測試:設計
<body ng-app="myApp"> <div id="main" ng-controller="myCtrl"> <button ng-click="add()">+1</button> <p>改變輸出值:</p> <input type="text" ng-model="testInfo.content"> <p>使用ng-bind綁定的標籤:</p> <p ng-bind="testInfo.content"></p> </div> <script src="./angular.min.js"></script> <script> angular.module('myApp', []) .controller('myCtrl', ['$scope', function($scope) { //初始化 $scope.testInfo = { content: 0 } $scope.add = function () { $scope.testInfo.content += 1; console.log($scope.testInfo.content); } }]); </script></body>
demo中,每次點擊+1按鈕,$scope.testInfo.content
的值會增長1,咱們能夠看到頁面上的結果:雙向綁定
1.3 刷視圖
來看看第一個活見鬼的例子,demo跟上面很相似,只是將鼠標點擊觸發的方式改爲了定時器自動觸發:
<body ng-app="myApp"> <div id="main" ng-controller="myCtrl"> <button ng-click="add()">+1</button> <p>改變輸出值:</p> <input type="text" ng-model="testInfo.content"> <p>使用ng-bind綁定的標籤:</p> <p ng-bind="testInfo.content"></p> </div> <script src="./angular.min.js"></script> <script> angular.module('myApp', []) .controller('myCtrl', ['$scope', function($scope) { //初始化 $scope.testInfo = { content: 0 } //定時自增 setInterval(function () { $scope.testInfo.content += 1; console.log('$scope.testInfo.content的值如今是:',$scope.testInfo.content); },1000) }]); </script></body>
你會發現,數據模型一直在變,可是頁面卻沒有刷新:
這裏就是 Angularjs1.X
雙向數據綁定中的第一個坑 ,你會發現$scope上綁定的數據模型和html中顯示的內容有時候並非實時關聯的。這其實和Angularjs1.X
的執行機制有關係。
若是咱們本身來考慮,javascript中有一個變量的值發生了變化,如今要將這個值同步到html頁面上,須要怎麼作呢?咱們須要獲取到這個DOM元素,而後改變它的innerHTML
屬性,若是是表單元素就修改value
。其實Angularjs
也是這樣作的,只不過使用了本身的封裝的方法——$apply()。那麼此處的問題其實就在於,在setInterval
的回調函數中去修改數據模型的值時,沒有觸發$apply()方法來更新視圖,而經過調用Angularjs
封裝的ng-*
方法(例如ng-click
點擊方法)來修改視圖模型時,會自動觸發$apply()方法,視圖也就同步刷新了。
解決方案1
使用Angularjs
封裝過的$interval
服務來實現定時任務,感興趣的讀者能夠本身看一下Angularjs
源碼中$intervalProvider
的部分,就會發如今方法最後的地方調用了$rootScope.$apply()
。
解決方案2
若是依然使用javascript原生的定時方法,那麼則須要在修改完視圖的數據模型後,手動調用$scope.$apply()
方法來將數據模型的變更同步到html頁面中。
除了controller與html中的雙向綁定,Angularjs
中還有另外一個雙向數據綁定,那就是controller與directive之間的綁定。綁定的形式有不少種,咱們先來看一下最多見的雙向綁定。
2.1 directive中的雙向數據綁定
在設定自定義指令的scope
參數時,將屬性的值設置爲=
就能夠實現雙向數據綁定,這裏API的解釋是:
父級controller中的指定變量會與自定義指令link函數中的變量相互影響。
下面的實例中,咱們將看看controller中的數據模型$scope.testInfo.content
的值與自定義指令中scope.pagination
如何相互影響,是否如定義所說這裏的綁定真的是雙向的。示例界面以下(demo源碼請見附件demo.html文件):
每次點擊+1
按鈕,Scope.testInfo.content
的值都會增長1
每次點擊show $scope.testInfo
,控制檯都會打印出$scope.testInfo
的值
每次點擊標籤上的數字,則會打印出自定義指令中scope.pagination
的值,並將該值進行自增
接下來的測試操做,咱們將按照以下的流程進行:
點擊5次+1
按鈕,再點擊5次數字標籤
點擊show $scope.testInfo
按鈕
2.2 怎麼又不刷新了
隨着上一節的操做步驟,咱們一塊兒來見證雙向數據綁定中又一次鬧鬼事件:
點擊5次+1
按鈕,再點擊5次數字標籤
結果爲:
咱們看到,第一次點擊數字標籤時,控制檯打出了link函數中scope.pagination
的值爲5,這說明$scope.testInfo.content
的值被傳遞給了自定義指令中的scope.pagination
,也就是說數據從controller流向了directive。而當咱們再點擊4次數字標籤(一共點了5次)後,從控制檯能夠看出,scope.pagination
的值已經成爲10,而頁面上使用ng-bind
指令獲取到的結果卻依舊是5。也就是說,數據從沒有從directive流向controller。是否是有一種被騙的感受?彆着急,接着看。
點擊show $scope.testInfo
按鈕
結果爲:
當咱們點擊show $scope.testInfo
時,控制檯打印出了$scope.testInfo.content的值爲5,這下證據坐實了,明明說好的雙向數據綁定,然而當自定義指令中的scope.pagination
改變時,$scope.testInfo.content
並無跟着一塊兒改變。But!!!!咱們會發現,這個show $scope.testInfo
點下去之後,頁面上經過ng-bind
綁定的值卻變成了10。也就是說,數據又從directive流回了controller。
官方建議使用$watch
方法來追蹤scope中的變量,而當咱們這樣作時,會發現$watch
函數僅能追蹤到那些經過修改controller中的數據模型而影響link函數中變量的行爲並更新視圖。
這裏就是 Angularjs1.X
雙向數據綁定中的第二個坑,controller和directive中所謂的雙向數據綁定,並不能追蹤指定變量的全部變化,並且不是同步完成的。
其實這裏的問題仍然和Angularjs
的運行機制有關,解決方案以下:
解決方案1
使用自定義指令的templateUrl
屬性替換當前指令的模板,使用ng-click
指令來綁定一個點擊響應函數,在響應函數中改變scope.piganation
的值。
解決方案2
在手動綁定的監聽回調中,修改自定義指令做用域內的變量後,使用scope.$emit( )
方法通知其父級controller,並在controller中使用$scope.$on( )
方法監聽同名事件,並修改對應的數據模型的值。
解決方案3
每當改變自定義指令中的變量值後,調用scope.$apply()
方法,將directive中的變量值同步至controller的數據模型以及頁面。
3.1 Angularjs中雙向數據綁定的基本原理
Angularjs中的雙向數據綁定,是經過一種叫作*"髒循環檢查(dirty-checking)"的機制實現的。
其基本過程是這樣的,每當咱們使用ng-model
或ng-bind
指令將數據模型中的某個變量值和html頁面上某個標籤的內容聯繫起來時,Angular就會把這些變量放進一個WatchCollection
的集合中,並自動幫咱們來監控這些變量。每當WatchCollection
中有變量出現變更時,Angular就會遍歷WatchCollection
來查看是否有其餘監控中的變量也被影響,每當有一個變量被影響,Angular都會在遍歷後再進行一次遍歷,直到某一次遍歷後WatchCollection
中的變量都沒有變化,則Angular會認爲當前的改動已經穩定了,而後纔會將數據模型的變化同步到DOM元素上去,也就實現了數據綁定。
咱們能夠把WatchCollection
理解爲當前頁面的一種抽象,其中包含着頁面上全部有可能發生變化的部分。
3.2 雙向數據綁定的實踐經驗
想要在Angularjs
項目中更加穩定地使用雙向數據綁定,筆者的建議是:
在Angularjs
項目中,儘量地使用Angular告訴你的方式去編寫所但願實現的功能。
咱們能夠回顧一下上面在使用雙向數據綁定發生異常時的場景:
使用了原生的定時器(Angular中你應該使用$interval
,$timeout
服務)
用類原生方法(bind)爲元素添加事件監聽器,並在回調函數中修改了變量的值(Angular中,你應該使用ng-click
來實現點擊事件的監聽)
...
你會發現,每當本身沒有按照Angular的方式去編寫代碼,或者沒有按照一個模塊設計的初衷去使用它時,就沒法確切地獲得指望的結果。這是很容易理解的,若是你沒有按照Angular要求的方式書寫代碼,憑什麼指望它對你的代碼作出100%正確的迴應呢?至於上述兩種數據綁定中出現問題的解決方案,上文已經有所說起,此處再也不贅述。
許多人都據說過"儘可能不要在controller中操做DOM"這句話,實際上它並不意味着你在controller中操做DOM會致使程序報錯,而是在說若是你同時使用jQuery
和Angular
兩套系統來管理本身的代碼,但又沒有按照官方指定的方式來規避它們之間的衝突,那代碼極可能會變得不穩定。想一想當年騰訊電腦管家和360安全衛士將你的電腦卡死的場景,你就明白這樣作的結果了。
筆者曾經看過這樣一段話,以爲深有感觸:
所謂高手,是指那些熟知套路
且創意無窮
的人。而高手之間的較量,歸根結底都是基本功
的比拼。
願有朝一日,你也能成爲高手。
來源:華爲雲社區原創 做者:大史不說話