Angular1.x中ngModel的$render的詳解

這篇文章是我兩年前在博客園寫的,如今移植過來,不過Angular 1.x 在國內用的人已經很少了,但願能幫助到有須要的人

在我開始着手 ngModel 的領域時候,有一個問題很令我糾結,那就是 $render 究竟是作什麼的呢?查了不少資料都只是簡單的描述一下,這就令我很糾結了,終於在一個陽光明媚的晚上,我終於解決了這個大問題app

那麼這個 $render 方法究竟是幹什麼的呢?他的用處就是在 $viewValue 改變的時候能夠從新綁定 model 數據,可是咱們要注意一點( $viewValue 和 DOM 節點的 value 是不一樣的),我以爲他們的區別有點相似 setTimeout$timeout 的區別,可是又不太同樣。PS:其實 modelValue 和綁定的數據也能夠不一樣async

Input裏面模型的值:{{vm.modelTest}}

<input type="text" ng-model="vm.modelTest" model-render>
.directive('modelRender',
  function () {
    return {
      require: 'ngModel',
      link: function (scope, iElm, iAttrs, ngModelCtrl) {
        iElm.on('mouseenter', function () {
          //嘗試註釋
          iElm.val(1);
          console.log(ngModelCtrl);

          //嘗試註釋
          ngModelCtrl.$setViewValue(11);
          console.log(ngModelCtrl);

          //嘗試註釋
          ngModelCtrl.$render();
          console.log(ngModelCtrl);
        })
      }
    }
  }
)

咱們分幾種狀況分析函數

這是鼠標沒有通過指令的時候的樣子oop

圖片描述

1.當咱們使用 js 原生方法設置 input 的 val 值的時候,而且不執行 $render 函數,咱們能夠看到 input 裏面的 model 值是沒有變化的,可是 input 的的 value 是變成了 1,並且咱們看到不只 model 值沒有變化,ngModel 的 $viewValue$modelValue 一樣也沒有變化。咱們能夠得出結論 (input 的 value 值不必定等於 $viewValue)測試

結果是這樣的ui

圖片描述

而後,咱們嘗試在執行 js 原生改變 value 值以後,執行 $render 函數是個什麼樣的情況,this

圖片描述
看完上面的實驗以後咱們發現 input 的 value 值並無發生變化,也就是說 js 原生改變 input 的 value 值是無效的,那麼在這裏咱們就能夠看到 $render 的功能了。spa

咱們能夠大膽的預計 $render 的功能跟 $apply 的功能是一致的,咱們在上一章講過,$apply 是以 viewValue 爲主,讓 modelValue 變成 viewValue,也就是 modelValue -> viewValue,那麼 $render 是否是以 modelValue 爲主,讓 viewValue->modelValue 呢?code

2.接下來咱們嘗試,使用 ng 原生改變 也就是說 $setviewValue,是如何表現的呢?orm

如今咱們註釋掉 js 原生改變 value 的方法,而去使用 $setViewValue,而且不執行 $render 函數,直接上結果,咱們看到,執行完 $setViewValue 以後,不管是 viewValue 和 modelValue 都是已經同步了,可是 input 裏面的值卻依然是 test,在這裏咱們再次驗證了那個說法( $viewValue 和 DOM 節點的 value 是不一樣的)

圖片描述

如今咱們在 $setViewValue 以後使用,$render() 看看是什麼效果,

圖片描述

你們發現了吧,$render 的功能和 $apply 的功能極爲類似,可是是否是不少人在講 $render 的時候,都會說 model 同步到 view,我以爲這個說法不太對,我測試過在 click 事件用很是規手段改變 controller 中 model 的值,發現就算 controller 的值已經改變了,可是 ngModel 的值不管是 viewValue 仍是 modelValue 都沒有變化,而後嘗試用 $modelValue 的屬性強行改變$modelValue,結果仍是沒做用。咱們下面來看看 $render 的源碼

ctrl.$render = function() {
  element.val(ctrl.$isEmpty(ctrl.$viewValue) ? '' : ctrl.$viewValue);
};

這是其中一個,$render 在不一樣的指令下的代碼都不太同樣,可是其做用基本一致,可是從這裏咱們就能夠看出 $render 的到底在幹什麼事了。那麼 $render 何時觸發呢?其實看你本身想何時調用它,你能夠覆蓋他的方法,重寫,在 $watch 也好,$viewChange 也好。默認的觸發事件一些特別 input 的 value 改變的時候例如單選,還有 rollbackView()的時候

另一個真正體現 $render 執行事件的源代碼在這裏,裏面我寫了註釋,你們應該都能懂,好了,$render 就已經講完了

$scope.$watch(function ngModelWatch() {
  //解析ngModel的表達式,獲取內容
  var modelValue = ngModelGet($scope);

  // if scope model value and ngModel value are out of sync
  // TODO(perf): why not move this to the action fn?
  //判斷表達式的值是否跟modelValue一致
  if (modelValue !== ctrl.$modelValue &&
    // checks for NaN is needed to allow setting the model to NaN when there's an asyncValidator
    (ctrl.$modelValue === ctrl.$modelValue || modelValue === modelValue)
  ) {
    //更新modelValue的值
    ctrl.$modelValue = ctrl.$$rawModelValue = modelValue;
    parserValid = undefined;
    //獲取管道信息
    var formatters = ctrl.$formatters,idx = formatters.length;

    var viewValue = modelValue;
    while (idx--) {
      viewValue = formatters[idx](viewValue);
    }
    //若是viewValue和ModelValue不一致
    if (ctrl.$viewValue !== viewValue) {
      ctrl.$viewValue = ctrl.$$lastCommittedViewValue = viewValue;
      ctrl.$render();

      ctrl.$$runValidators(modelValue, viewValue, noop);
    }
  }
  //返回解析的表達式
  return modelValue;
});
相關文章
相關標籤/搜索