在angular1項(xiàng)目中充包,有的時(shí)候我們需要寫(xiě)自定義directive
來(lái)完成某些應(yīng)用邏輯呛梆,但是如果你有過(guò)一些的angular1項(xiàng)目的開(kāi)發(fā)經(jīng)驗(yàn)杭抠,肯定會(huì)因?yàn)橐恍┠涿顢?shù)據(jù)雙向綁定羊始,回調(diào)函數(shù)無(wú)法調(diào)用執(zhí)行等問(wèn)題谱俭,困擾過(guò)。下面是一個(gè)簡(jiǎn)單的案例啄骇。
問(wèn)題引出
這里我們寫(xiě)一個(gè)簡(jiǎn)單的directive
來(lái)說(shuō)明問(wèn)題痴鳄。
directive.js
angular.module('app.directive')
.directive('ayTest',function(){
return {
restrict: 'EA',
scope: {
name:'='
},
template:`
<div class="padding mystyle">
{{vm.name}}
</div>
`,
controller:function($scope){
var vm = $scope.vm = {};
vm.name = $scope.name;
}
}
});
A.controller.js
var vm = $scope.vm = {};
vm.name = 'Tom1';
$timeout(function(){
vm.name = 'Tom2';
},1000);
$timeout(function(){
vm.name = 'Tom3';
},2000);
A.html
<ay-test name="vm.name"></ay-test>
這個(gè)directive可以實(shí)現(xiàn)以我們自己的格式展示‘name’的功能,我們可以通過(guò)在template中填寫(xiě)自己獨(dú)有的style或者嵌套div來(lái)完善name展示模塊缸夹。
在這我們希望name
能夠?qū)崿F(xiàn)雙向綁定痪寻,即當(dāng)在A頁(yè)面中name
值發(fā)生變化的時(shí)候,directive也自動(dòng)獲得更改虽惭。
但是橡类,在這里并不能實(shí)現(xiàn)這個(gè)功能,當(dāng)name
顯示為Tom1
之后趟妥,就不會(huì)再變化了。
為什么會(huì)這樣呢佣蓉?
我們都知道自定義directive時(shí)披摄,有3個(gè)綁定符號(hào),分別是
-
@
單向綁定屬性值 -
=
雙向綁定屬性 -
&
綁定用戶(hù)函數(shù)
這里為了實(shí)現(xiàn)信息的雙向綁定勇凭,已經(jīng)使用了=
疚膊,為什么還無(wú)法完成雙向綁定呢?對(duì)于大神們虾标,當(dāng)然能一眼看出問(wèn)題的所在寓盗,但小白可能會(huì)摸索比較長(zhǎng)的時(shí)間~~~~
問(wèn)題出在vm.name = $scope.name;
這句代碼上。因?yàn)閚ame是一個(gè)基本字符串璧函,賦值時(shí)傀蚌,相當(dāng)于對(duì)vm.name創(chuàng)建了一個(gè)新值,并賦值為$scope.name
的值蘸吓。這樣善炫,雖然controller中的$scope.name
和頁(yè)面上的{{name}}
仍存在雙向綁定,但是和我們的vm.name
卻無(wú)任何關(guān)系了库继。
我們應(yīng)該還知道下面的這個(gè)問(wèn)題:
//controller.js
$socpe.username = 'Mary';
//B.html
<input ng-model='username' />
如果你按照上面的方式來(lái)編碼箩艺,十有八九會(huì)出現(xiàn)意外情況。
出現(xiàn)這樣錯(cuò)誤的根本原因宪萄,其實(shí)還是對(duì)JavaScript變量的值訪問(wèn)艺谆、引用訪問(wèn),開(kāi)發(fā)中的值傳遞拜英、引用傳遞沒(méi)有理解清楚静汤。下面說(shuō)一些這方面的問(wèn)題。
Js基本類(lèi)型、值傳遞
下面是筆者摘自《JavaScript高級(jí)程序設(shè)計(jì)》中的幾段話(huà)撒妈,讀者可以參照理解恢暖。這里并不是筆者自己懶,而書(shū)中的表達(dá)更準(zhǔn)確狰右,而且也很容易理解~~(如果你說(shuō)不知道這本書(shū)杰捂,推薦快去讀一下,如果你做Js相關(guān)開(kāi)發(fā)棋蚌,會(huì)讓你受益匪淺嫁佳,我能給你電子版,聯(lián)系hao5743@163.com
)
5種基本數(shù)據(jù)類(lèi)型:Undefined谷暮、Null蒿往、Boolean、Number和String湿弦,5種基本數(shù)據(jù)類(lèi)型是按值訪問(wèn)的瓤漏,因?yàn)榭梢圆僮鞅4嬖谧兞恐械膶?shí)際的值。
引用類(lèi)型的值是保存在內(nèi)存中的對(duì)象颊埃。于其他語(yǔ)言不同蔬充,JavaScript不允許直接訪問(wèn)內(nèi)存中的位置,也就是說(shuō)不能直接操作對(duì)象的內(nèi)存空間班利。在操作對(duì)象時(shí)饥漫,實(shí)際上是在操作對(duì)象的引用而不是實(shí)際的對(duì)象。引用類(lèi)型的值是按引用訪問(wèn)的罗标。
--參見(jiàn)《JavaScript高級(jí)程序設(shè)計(jì)》P69 4.1 基本類(lèi)型和引用類(lèi)型的值
如果從一個(gè)變量向另一個(gè)變量復(fù)制基本類(lèi)型的值庸队,會(huì)在變量對(duì)象上創(chuàng)建一個(gè)新值,然后把該值復(fù)制到為新變量分配的位置上闯割。
--參見(jiàn)《JavaScript高級(jí)程序設(shè)計(jì)》P69 4.1.2 復(fù)制變量值
ECMAScript中所有函數(shù)的參數(shù)都是按值傳遞的彻消。也就是說(shuō),把函數(shù)外部的值賦值給函數(shù)內(nèi)部的參數(shù)宙拉,就和把值從一個(gè)變量復(fù)制到另一個(gè)變量一樣证膨。基本類(lèi)型值的傳遞如同基本類(lèi)型變量的復(fù)制一樣鼓黔,二引用類(lèi)型值的傳遞央勒,則如同引用類(lèi)型變量的復(fù)制一樣。有不少開(kāi)發(fā)人員在這一點(diǎn)可能會(huì)感到困惑澳化,因?yàn)樵L問(wèn)變量有按值和按引用兩種方式崔步,而參數(shù)只能按值傳遞。
--參見(jiàn)《JavaScript高級(jí)程序設(shè)計(jì)》P70 4.1.3 傳遞參數(shù)
解決方案
理解了上面的話(huà)缎谷,那么你就能輕易用不同的方案解決這個(gè)問(wèn)題了井濒。
方案1 直接使用$scope.name
展示數(shù)據(jù)
directive.js
angular.module('app.directive')
.directive('ayTest',function(){
return {
restrict: 'EA',
scope: {
name:'='
},
//這里直接使用name灶似,不再使用vm.name
template:`
<div class="padding mystyle">
{{name}}
</div>
`,
controller:function($scope){
var vm = $scope.vm = {};
}
}
});
不再使用vm的方式,而是直接綁定到頁(yè)面瑞你,可以解決問(wèn)題酪惭。
方案2 使用引用類(lèi)型打包基本類(lèi)型再傳遞
如果,你在angular1開(kāi)發(fā)中者甲,一直遵循了好的實(shí)踐春感,那么有可能vm.name
你可能已經(jīng)使用習(xí)慣了。也許你會(huì)問(wèn)虏缸,如果我還想使用vm.name
的方式鲫懒,我想遵循官方給出的最佳實(shí)踐,怎么解決這個(gè)問(wèn)題呢刽辙?
(什么是最佳實(shí)踐窥岩?如果你是angular開(kāi)發(fā)者,而還不知道的話(huà)宰缤,那么一定要去讀讀颂翼,原版在這里,英文差的看這里)
當(dāng)然有方法啦慨灭!看下面
controller.js
var vm = $scope.vm = {};
vm.name = {};
//將數(shù)據(jù)封裝到到內(nèi)部屬性data上
vm.name.data = 'Tom1';
$timeout(function(){
vm.name.data = 'Tom2';
},1000);
$timeout(function(){
vm.name.data = 'Tom3';
},2000);
A.html
<ay-test name="vm.name"></ay-test>
directive.js
.directive('ayTest',function(){
return {
restrict: 'EA',
scope: {
name:'='
},
template:`
<div class="padding">
{{vm.name.data}}
</div>
`,
controller:function($scope){
var vm = $scope.vm = {};
//注意朦乏,雖然這里重新賦值了name,但是我們的vm.name.data還是同一份,所以仍能實(shí)現(xiàn)綁定
vm.name = $scope.name;
}
}
});
方案3 使用$scope.$watch
如果你知道$scope.$watch
缘挑,并且了解何時(shí)使用它集歇,那么你應(yīng)該知道這里也可能使用它來(lái)解決桶略∮锾裕看代碼。
controller.js
var vm = $scope.vm = {};
//并沒(méi)有進(jìn)行數(shù)據(jù)封裝
vm.name = 'Tom1';
$timeout(function(){
vm.name = 'Tom2';
},1000);
$timeout(function(){
vm.name = 'Tom3';
},2000);
A.html
<ay-test name="vm.name"></ay-test>
directive.js
angular.module('app.directive')
.directive('ayTest',function(){
return {
restrict: 'EA',
scope: {
name:'='
},
template:`
<div class="padding">
{{vm.name}}
</div>
`,
controller:function($scope){
var vm = $scope.vm = {};
//添加數(shù)據(jù)監(jiān)測(cè)
$scope.$watch('name',function(newValue,oldValue){
vm.name = $scope.name;
});
}
}
});
這個(gè)方法其實(shí)相當(dāng)于多加了一層的監(jiān)測(cè)际歼,vm.name
和頁(yè)面上的{{vm.name}}
綁定惶翻,通過(guò)$scope.$watch
,我們讓vm.name
和$scope.name
兩個(gè)變量的值保持一致性鹅心。
哪個(gè)方法好呢
個(gè)人推薦第一種方法吕粗,因?yàn)樗龅墓ぷ髯钌伲氏鄬?duì)較高(雖然高不了很多)旭愧。
最后的話(huà)
本文首先提出了在編寫(xiě)自定義directive時(shí)有時(shí)候會(huì)出現(xiàn)的數(shù)據(jù)無(wú)法同步的問(wèn)題颅筋。隨后分析了問(wèn)題,并給出了幾種解決方案输枯。
其實(shí)议泵,問(wèn)題的根本首先在于對(duì)JavaScript變量值訪問(wèn)
、引用訪問(wèn)
和值傳遞
等概念的理解桃熄,其次在于對(duì)angular1scope層級(jí)
先口、雙向綁定
、臟值檢測(cè)
等概念的理解。
有時(shí)碉京,還能需要處理其他情況厢汹,比如默認(rèn)值處理,分條件展示谐宙,錯(cuò)誤值處理烫葬、數(shù)據(jù)回顯、回調(diào)函數(shù)處理等具體問(wèn)題卧惜,可以參考上面3種思路來(lái)具體實(shí)現(xiàn)厘灼。
很簡(jiǎn)單一個(gè)問(wèn)題,個(gè)人只是做了一下分析咽瓷,有錯(cuò)誤的地方請(qǐng)大神及時(shí)指出设凹,感激不盡~~
Author:shaochong
Email:hao5743@163.com
文章地址:https://hao5743.github.io/2016/12/05/Data-bind-problem-in-Angular-when-define-your-own-directives/