angular中指令被用的最多的除了ng-repeat之外就非他莫屬了吧,由於各種業務的需求,我們經常需要去寫一些繼承ngModel的指令,來滿足各種奇葩需求,最多的莫過於表單的驗證啦,另外你在封裝一些jquery插件的時候,也是需要繼承ngModel的,最典型的莫過於 datepicker、fileUpload等等。
但是ngModel本身其實有很多坑,在這里分享給大家,如果大家又遇到其他問題,歡迎補充:
首先我們要知道一些概念的東西,這里我就不占篇幅了,不了解的可以先去看看這篇文章:
http://www.cnblogs.com/liulangmao/p/4110137.html
在這里,我基本分為兩種問題,一種是viewValue和modelValue不同的問題,另一種是管道問題
第一種:viewValue和modelValue不同的問題
1.viewValue想變成modelValue
2.modelValue想變成ViewValue
3.moduleValue想和viewModel不一樣
4.viewModel和DOM元素的值不一樣
要想解決這些問題,我們先來了解一些$apply的一個問題,那就是執行$apply之后數據是以什么為主的
①viewValue -> modelValue
②modelValue -> viewValue,於是我們寫了下面這個指令
<input type="text" ng-model="vm.modelTest" model-test>
.directive('modelTest',function(){ return { require : 'ngModel', link : function(scope,iElm,iAttrs,ngModelCtrl){ iElm.on('mouseenter',function(){ console.info("打印出更改之后,沒有執行$apply的值") console.log(ngModelCtrl); //更改modelValue的值 ngModelCtrl.$modelValue = "test change"; console.info("打印出更改之后,沒有執行$apply的值") console.log(ngModelCtrl); //執行$apply scope.$apply(); console.info("打印出更改之后,執行$apply的值") console.log(ngModelCtrl); }) } } })
從上面的指令,我們不難得出一個比較武斷的結論 $apply會根據viewValue的值而改變modelValue的值,也就是modelValue -> viewValue,那么,我們可以開始解決我們的問題了。
1.viewValue想變成modelValue
這種情況很少, 但是如果你在jq的操作就可能發生這種需求,一般依然還是$apply讓model的值重新更新,使用了$apply就會觸發$render()函數,因為modelValue的變化,會導致$render觸發
2.modelValue想變成ViewValue (看了上面的實驗,我想你應該懂了,直接使用$apply)
3.moduleValue想和viewModel不一樣
一般情況都會使用管道去解決,但是有個其他思路可以提供給大家,下面這段代碼是在國外看到的,覺得挺不錯的,效果大家可以去嘗試下,將model的值變成數組。
ngModelCtrl.$viewChangeListeners.push(function(){ $parse(iAttrs.ngModel).assign(scope, ngModelCtrl.$viewValue.split(',')); });
第二種:管道問題
管道是什么呢,在ngModel里面其實就是view視圖和model視圖數據交互的時候需要經過的數組或對象,這個數組或對象由方法組成,每次經過都會執行數組里面的方法,並返回結果。一共有四種管道
$formatters :它是model視圖轉向view視圖的管道(數組),model的值會經過他才轉變成view視圖的值,另外它的調用順序是最特別的,從后往前調用,,越在數組后面,越早調用。
$parsers : 它正好是跟$formatters相反,是view視圖轉向model視圖的管道(數組),view的值會經過他才轉變成model視圖的值,在1.2.x(兼容IE8)之前一般我們會在這里實現或擴展驗證功能
$validators:這是在1.3.x之后出現的管道(json對象),來幫助我們實現和擴展驗證功能的,當我們進入$parsers的同時,同時也會進入$validators,在這里我貼段源碼(驗證required)給大家看看,我想大家應該能理解這個管道的功能
ctrl.$validators.required = function(modelValue, viewValue) { return !attr.required || !ctrl.$isEmpty(viewValue); };
$asyncValidators :功能與$validators相似,不同的是他可以實現異步驗證,你可以在管道內部執行異步處理,會在結果返回之后,才去設置驗證結果,這是個大課題,我看到有人討論過,我也就不詳細講了http://www.cnblogs.com/liulangmao/p/4118868.html可以進去這個博客看看。
好了,ngModel的一些東西基本已將講完了,另外還有一些就不仔細去說了,我的博客里面還有一篇關於$render的詳解,有興趣的可以了解下。
此帖是原創,轉載請注明