這算是一篇個人對angularjs的理解筆記吧,這裡有view model的同步過程,寫給大家看看吧,現在就讓我們一起進入本篇文章吧
事情起源於在專案中遇到的一個小問題:專案中需要一個輸入框輸入賣出產品數量,並且在使用者輸入後根據輸入資料計算手續費。很自然的我用了ng-model和ng-change,而且一般情況下沒什麼問題。問題是:輸入框下方還有一個按鈕是全部賣出,點選這個按鈕程式會自動設定賣出額。但實際上這時程式並沒有計算手續費。
經過排查並查閱文件之後,發現是ng-change的問題。 Angular關於ng-change的官方文件的提示是:
The expression is not evaluated when the value change is coming from the model.
ng-change的源碼也很簡單:
var ngChangeDirective = valueFn({ restrict: 'A', require: 'ngModel', link: function(scope, element, attr, ctrl) { ctrl.$viewChangeListeners.push(function() { scope.$eval(attr.ngChange); }); } });
從中我們也可以看出ng-change只做了view到model的監聽。所以當我們直接在js修改ng-model的變數時並不會觸發ng-change。
問題找到了,解決方案也不難,放棄ng-change,改用$watch就行了。
但是就這麼結束了嗎?一個變數從view變化開始到同步更新到model到底經歷了什麼?反過來呢,是一樣的嗎?
所以我又去看了看ng-model的源碼,並沒有什麼收穫,不過意外的了解到了這麼個點:
ng-change是在model值變化之前執行的。 ng-model原始碼中有這麼個函數:
function setupModelWatcher(ctrl) { // model -> value // !!!Note: we cannot use a normal scope.$watch as we want to detect the following: // !!!1. scope value is 'a' // !!! 2. user enters 'b' // !!!3. ng-change kicks in and reverts scope value to 'a' // -> scope value did not change since the last digest as // ng-change executes in apply phase // !!!4. view should be changed back to 'a' ctrl.$$scope.$watch(function ngModelWatch(scope) { var modelValue = ctrl.$$ngModelGet(scope); // if scope model value and ngModel value are out of sync // This cannot be moved to the action function, because it would not catch the // case where the model is changed in the ngChange function or the model setter if (modelValue !== ctrl.$modelValue && // checks for NaN is needed to allow setting the model to NaN when there's an asyncValidator // eslint-disable-next-line no-self-compare (ctrl.$modelValue === ctrl.$modelValue || modelValue === modelValue) ) { ctrl.$$setModelValue(modelValue); } return modelValue; }); }
裡面的註解解釋了為什麼變數model值的修改要在ng-change之後,因為ng-change中很可能會把變數的值又修改回去,這樣變數值事實上就沒改變(寫api真的是什麼情況都要考慮到啊!!)。關於這一點,以及前面的問題這裡有一個demo程式碼:http://php.cn/course/47.html
既然看原始碼沒什麼收穫,那就上網搜搜文章看看吧。這個過程中找到一篇很好的文章,這篇文章介紹了
$formatters
,$parsers
,$render
以及$setViewValue
。這裡就不再介紹了,如果需要學習,原文在這裡:http://php.cn/course/47.html
在學習$setViewValue
時也發現一個很容易被坑的點:在呼叫$setViewValue
時,如果參數是引用變量,那麼如果引用變數位址沒變,則這個變數被認為沒有改變,如var map = ['er', 'tr '];那麼map.pop();之後$setViewValue不認為map值改變了。關於這個具體可以看我對這個問題的回答。 (想看更多就到PHP中文網AngularJS開發手冊中學習)
ng-model也有這個問題,這個在ng-model原始碼註解中可以看到:
However, custom controls might also pass objects to this method. In#從上面也可以看到其實一個變數的更新由view到model和model到view不只
this case, we should make a copy of the object before passing it to$setViewValue
. This is becausengModel
does not perform a deep
watch of objects, it only looks for a change of identity.If you only change the property of the object then Model will willnotngize that the object has changed and will not invoke the
$parsersand
$validatorspipelines.
$formatters和
$parsers管道,那麼還有哪些呢?
這個回答中有個demo鏈接,我copy了一下並做了寫小修改放在這個地址了:http://php.cn/course/47.html,這個demo很清晰的顯示了變量更新的過程,細節就不再累述了,這裡只把結果總結如下:
從model到view:
model值修改---->
$formatters管道--- ->
$render函數---->
$validators ---->
$watch函數
view值修改---->
$setViewValue函數---->
$parsers管道---->
$validators ---->
$viewChangeListener函數---->
$watch函數
我們也可以直接呼叫
$setViewValue函數去直接改變
$viewValue 的值,流程會跟上面一樣。
注意在使用
$setViewValue時一定要警惕參數是引用變數的情況,這個坑在上文也已經提到了。
$formatters 和
$parsers 管道,關於這部分可以參考文中給出的連結
好了,這篇文章到這就結束了(想看更多就到PHP中文網AngularJS使用手冊中學習),有問題的可以在下方留言提問。
#以上是angular1學習筆記,裡面有angularjs中的view model同步過程的詳細內容。更多資訊請關注PHP中文網其他相關文章!