您好,登錄后才能下訂單哦!
本文首發(fā)于華為云社區(qū)
原文鏈接:https://bbs.huaweicloud.com/blogs/e19336d0889f11e89fc57ca23e93a89f
聊聊 Angularjs1.x中那些活見鬼的事情。
html-Controller的雙向數(shù)據(jù)綁定,在開發(fā)中非常常見,也是Angularjs1.x的宣傳點之一,使用中并沒有太多問題。
也就是從視圖層流向模型層,原生html中需要使用表單元素(例如input
標簽)來收集用戶輸入信息,Angularjs中通過在表單元素上使用ng-model
標簽,當用戶輸入信息時,同步將用戶輸入的信息賦值給controller中的變量:
<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即可看到,每次在頁面輸入數(shù)字后,控制臺輸出的$scope,testInfo.content
的值都和頁面保持一致:
也就是從模型層流向數(shù)據(jù)層,當controller中的數(shù)據(jù)模型變量發(fā)生變化后,Angularjs又會根據(jù)數(shù)據(jù)模型的值去改變ng-model
指令綁定的表單元素的值,使用ng-bind
指令也可以被動獲得來自controller的數(shù)據(jù)流。
我們編寫如下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,我們可以看到頁面上的結(jié)果:
來看看第一個活見鬼的例子,demo跟上面很類似,只是將鼠標點擊觸發(fā)的方式改成了定時器自動觸發(fā):
<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的值現(xiàn)在是:',$scope.testInfo.content);
},1000)
}]);
</script>
</body>
你會活見鬼地發(fā)現(xiàn),數(shù)據(jù)模型一直在變,但是頁面卻沒有刷新:
這里就是
Angularjs1.X
雙向數(shù)據(jù)綁定中的第一個坑 ,你會發(fā)現(xiàn)$scope上綁定的數(shù)據(jù)模型和html中顯示的內(nèi)容有時候并不是實時關(guān)聯(lián)的。這其實和Angularjs1.X
的執(zhí)行機制有關(guān)系。
如果我們自己來考慮,javascript中有一個變量的值發(fā)生了變化,現(xiàn)在要將這個值同步到html頁面上,需要怎么做呢?我們需要獲取到這個DOM元素,然后改變它的innerHTML
屬性,如果是表單元素就修改value
。其實Angularjs
也是這樣做的,只不過使用了自己的封裝的方法——$apply()。那么此處的問題其實就在于,在setInterval
的回調(diào)函數(shù)中去修改數(shù)據(jù)模型的值時,沒有觸發(fā)$apply()方法來更新視圖,而通過調(diào)用Angularjs
封裝的ng-*
方法(例如ng-click
點擊方法)來修改視圖模型時,會自動觸發(fā)$apply()方法,視圖也就同步刷新了。
解決方案1
使用Angularjs
封裝過的$interval
服務(wù)來實現(xiàn)定時任務(wù),感興趣的讀者可以自己看一下Angularjs
源碼中$intervalProvider
的部分,就會發(fā)現(xiàn)在方法最后的地方調(diào)用了$rootScope.$apply()
。
解決方案2
如果依然使用javascript原生的定時方法,那么則需要在修改完視圖的數(shù)據(jù)模型后,手動調(diào)用$scope.$apply()
方法來將數(shù)據(jù)模型的變動同步到html頁面中。
除了controller與html中的雙向綁定,Angularjs
中還有另一個雙向數(shù)據(jù)綁定,那就是controller與directive之間的綁定。綁定的形式有很多種,我們先來看一下最常見的雙向綁定。
在設(shè)定自定義指令的scope
參數(shù)時,將屬性的值設(shè)置為=
就可以實現(xiàn)雙向數(shù)據(jù)綁定,這里API的解釋是:
父級controller中的指定變量會與自定義指令link函數(shù)中的變量相互影響。
下面的實例中,我們將看看controller中的數(shù)據(jù)模型$scope.testInfo.content
的值與自定義指令中scope.pagination
如何相互影響,是否如定義所說這里的綁定真的是雙向的。示例界面如下(demo源碼請見附件demo.html文件):
+1
按鈕,Scope.testInfo.content
的值都會增加1show $scope.testInfo
,控制臺都會打印出$scope.testInfo
的值scope.pagination
的值,并將該值進行自增接下來的測試操作,我們將按照如下的流程進行:
+1
按鈕,再點擊5次數(shù)字標簽show $scope.testInfo
按鈕隨著上一節(jié)的操作步驟,我們一起來見證雙向數(shù)據(jù)綁定中又一次鬧鬼事件:
點擊5次+1
按鈕,再點擊5次數(shù)字標簽
結(jié)果為:
我們看到,第一次點擊數(shù)字標簽時,控制臺打出了link函數(shù)中scope.pagination
的值為5,這說明$scope.testInfo.content
的值被傳遞給了自定義指令中的scope.pagination
,也就是說數(shù)據(jù)從controller流向了directive。而當我們再點擊4次數(shù)字標簽(一共點了5次)后,從控制臺可以看出,scope.pagination
的值已經(jīng)成為10,而頁面上使用ng-bind
指令獲取到的結(jié)果卻依舊是5。也就是說,數(shù)據(jù)從沒有從directive流向controller。是不是有一種被騙的感覺?別著急,接著看。
點擊show $scope.testInfo
按鈕
結(jié)果為:
當我們點擊show $scope.testInfo
時,控制臺打印出了$scope.testInfo.content的值為5,這下證據(jù)坐實了,明明說好的雙向數(shù)據(jù)綁定,然而當自定義指令中的scope.pagination
改變時,$scope.testInfo.content
并沒有跟著一起改變。But?。。?!我們會發(fā)現(xiàn),這個show $scope.testInfo
點下去以后,頁面上通過ng-bind
綁定的值卻變成了10。也就是說,數(shù)據(jù)又從directive流回了controller。
官方建議使用$watch
方法來追蹤scope中的變量,而當我們這樣做時,會發(fā)現(xiàn)$watch
函數(shù)僅能追蹤到那些通過修改controller中的數(shù)據(jù)模型而影響link函數(shù)中變量的行為并更新視圖。
這里就是
Angularjs1.X
雙向數(shù)據(jù)綁定中的第二個坑,controller和directive中所謂的雙向數(shù)據(jù)綁定,并不能追蹤指定變量的所有變化,而且不是同步完成的。
其實這里的問題仍然和Angularjs
的運行機制有關(guān),解決方案如下:
解決方案1
使用自定義指令的templateUrl
屬性替換當前指令的模板,使用ng-click
指令來綁定一個點擊響應(yīng)函數(shù),在響應(yīng)函數(shù)中改變scope.piganation
的值。
解決方案2
在手動綁定的監(jiān)聽回調(diào)中,修改自定義指令作用域內(nèi)的變量后,使用scope.$emit( )
方法通知其父級controller,并在controller中使用$scope.$on( )
方法監(jiān)聽同名事件,并修改對應(yīng)的數(shù)據(jù)模型的值。
解決方案3
每當改變自定義指令中的變量值后,調(diào)用scope.$apply()
方法,將directive中的變量值同步至controller的數(shù)據(jù)模型以及頁面。
Angularjs中的雙向數(shù)據(jù)綁定,是通過一種叫做*"臟循環(huán)檢查(dirty-checking)"的機制實現(xiàn)的。
其基本過程是這樣的,每當我們使用ng-model
或ng-bind
指令將數(shù)據(jù)模型中的某個變量值和html頁面上某個標簽的內(nèi)容聯(lián)系起來時,Angular就會把這些變量放進一個WatchCollection
的集合中,并自動幫我們來監(jiān)控這些變量。每當WatchCollection
中有變量出現(xiàn)變動時,Angular就會遍歷WatchCollection
來查看是否有其他監(jiān)控中的變量也被影響,每當有一個變量被影響,Angular都會在遍歷后再進行一次遍歷,直到某一次遍歷后WatchCollection
中的變量都沒有變化,則Angular會認為當前的改動已經(jīng)穩(wěn)定了,然后才會將數(shù)據(jù)模型的變化同步到DOM元素上去,也就實現(xiàn)了數(shù)據(jù)綁定。
我們可以把WatchCollection
理解為當前頁面的一種抽象,其中包含著頁面上所有有可能發(fā)生變化的部分。
想要在Angularjs
項目中更加穩(wěn)定地使用雙向數(shù)據(jù)綁定,筆者的建議是:
在
Angularjs
項目中,盡可能地使用Angular告訴你的方式去編寫所希望實現(xiàn)的功能。
我們可以回顧一下上面在使用雙向數(shù)據(jù)綁定發(fā)生異常時的場景:
$interval
,$timeout
服務(wù))ng-click
來實現(xiàn)點擊事件的監(jiān)聽)你會發(fā)現(xiàn),每當自己沒有按照Angular的方式去編寫代碼,或者沒有按照一個模塊設(shè)計的初衷去使用它時,就無法確切地得到期望的結(jié)果。這是很容易理解的,如果你沒有按照Angular要求的方式書寫代碼,憑什么期望它對你的代碼做出100%正確的回應(yīng)呢?至于上述兩種數(shù)據(jù)綁定中出現(xiàn)問題的解決方案,上文已經(jīng)有所提及,此處不再贅述。
許多人都聽說過"盡量不要在controller中操作DOM"這句話,實際上它并不意味著你在controller中操作DOM會導(dǎo)致程序報錯,而是在說如果你同時使用jQuery
和Angular
兩套系統(tǒng)來管理自己的代碼,但又沒有按照官方指定的方式來規(guī)避它們之間的沖突,那代碼很可能會變得不穩(wěn)定。想想當年騰訊電腦管家和360安全衛(wèi)士將你的電腦卡死的場景,你就明白這樣做的結(jié)果了。
筆者曾經(jīng)看過這樣一段話,覺得深有感觸:
所謂高手,是指那些
熟知套路
且創(chuàng)意無窮
的人。而高手之間的較量,歸根結(jié)底都是基本功
的比拼。
愿有朝一日,你也能成為高手。
本文首發(fā)于華為云社區(qū)
原文鏈接:https://bbs.huaweicloud.com/blogs/e19336d0889f11e89fc57ca23e93a89f
免責聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。