这篇文章是我两年前在博客园写的,现在移植过来,不过Angular 1.x 在国内用的人已经不多了,希望能帮助到有需要的人
在我开始着手 ngModel 的领域时候,有一个问题很令我纠结,那就是 $render
到底是做什么的呢?查了很多资料都只是简单的描述一下,这就令我很纠结了,终于在一个阳光明媚的晚上,我终于解决了这个大问题
那么这个 $render
方法到底是干什么的呢?他的用处就是在 $viewValue
改变的时候可以重新绑定 model 数据,但是我们要注意一点( $viewValue
和 DOM 节点的 value 是不同的),我觉得他们的区别有点类似 setTimeout
和 $timeout
的区别,但是又不太一样。PS:其实 modelValue 和绑定的数据也可以不同
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);
})
}
}
}
)
我们分几种情况分析
这是鼠标没有经过指令的时候的样子
1.当我们使用 js 原生方法设置 input 的 val 值的时候,并且不执行 $render
函数,我们可以看到 input 里面的 model 值是没有变化的,但是 input 的的 value 是变成了 1,而且我们看到不仅 model 值没有变化,ngModel 的 $viewValue
和 $modelValue
同样也没有变化。我们可以得出结论 (input 的 value 值不一定等于 $viewValue
)
结果是这样的
然后,我们尝试在执行 js 原生改变 value 值之后,执行 $render
函数是个什么样的状况,
看完上面的实验之后我们发现 input 的 value 值并没有发生变化,也就是说 js 原生改变 input 的 value 值是无效的,那么在这里我们就可以看到 $render
的功能了。
我们可以大胆的预计 $render
的功能跟 $apply
的功能是一致的,我们在上一章讲过,$apply
是以 viewValue 为主,让 modelValue 变成 viewValue,也就是 modelValue -> viewValue,那么 $render
是不是以 modelValue 为主,让 viewValue->modelValue 呢?
2.接下来我们尝试,使用 ng 原生改变 也就是说 $setviewValue
,是如何表现的呢?
现在我们注释掉 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;
});
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。