总结一下问题,ngModelController
之前要经过一个过程watches
将被解雇。您正在记录外部$scope
之前的财产ngModelController
已处理更改并导致 $digest 循环,这将依次触发$watchers
。我不会考虑model
更新到那时。
这是一个复杂的系统。我做了这个demo作为参考。我建议更改return
值、键入和单击 - 只是以各种方式摆弄它并检查日志。这使得一切如何运作很快变得清晰起来。
演示(玩得开心!)
ngModelController
有它自己的函数数组来运行作为对不同变化的响应。
ngModelController
有两种“管道”来确定如何处理某种变更。这些允许开发人员控制值的流动。
如果范围属性指定为ngModel
变化,则$formatter
管道将运行。该管道用于确定值如何来自$scope
应显示在视图中,但保留模型。所以,ng-model="foo"
and $scope.foo = '123'
,通常会显示123
在输入中,但格式化程序可能返回1-2-3
或任何值。$scope.foo
仍然是 123,但它显示为格式化程序返回的内容。
$parsers
处理同样的事情,但相反。当用户输入内容时,$parser 管道就会运行。无论什么$parser
返回值将被设置为ngModel.$modelValue
。所以,如果用户输入abc
和$parser
回报a-b-c
,那么视图不会改变,但是$scope.foo
now is a-b-c
.
在任一之后$formatter
or $parser
runs, $validators
将被运行。用于验证器的任何属性名称的有效性将由验证函数的返回值设置(true
or false
).
$viewChangeListeners
在视图更改后触发,而不是模型更改后触发。这一点特别令人困惑,因为我们指的是$scope.foo
并不是ngModel.$modelValue
。视图不可避免地会更新ngModel.$modelValue
(除非在管道中被阻止),但这不是model change
我们指的是。基本上,$viewChangeListeners
被解雇后$parsers
并且不是之后$formatters
。因此,当视图值发生变化(用户类型)时,$parsers, $validators, then $viewChangeListeners
。欢乐时光=D
所有这一切都发生在内部ngModelController
。在此过程中,ngModel
对象没有像您预期的那样更新。管道正在传递将影响该对象的值。在该过程结束时,ngModel
对象将被更新为正确的$viewValue
and $modelValue
.
最后,ngModelController
已完成,并且$digest
将发生循环以允许应用程序的其余部分响应所产生的更改。
这是演示中的代码,以防万一发生任何情况:
<form name="form">
<input type="text" name="foo" ng-model="foo" my-directive>
</form>
<button ng-click="changeModel()">Change Model</button>
<p>$scope.foo = {{foo}}</p>
<p>Valid: {{!form.foo.$error.test}}</p>
JS:
angular.module('myApp', [])
.controller('myCtrl', function($scope) {
$scope.foo = '123';
console.log('------ MODEL CHANGED ($scope.foo = "123") ------');
$scope.changeModel = function() {
$scope.foo = 'abc';
console.log('------ MODEL CHANGED ($scope.foo = "abc") ------');
};
})
.directive('myDirective', function() {
var directive = {
require: 'ngModel',
link: function($scope, $elememt, $attrs, $ngModel) {
$ngModel.$formatters.unshift(function(modelVal) {
console.log('-- Formatter --', JSON.stringify({
modelVal:modelVal,
ngModel: {
viewVal: $ngModel.$viewValue,
modelVal: $ngModel.$modelValue
}
}, null, 2))
return modelVal;
});
$ngModel.$validators.test = function(modelVal, viewVal) {
console.log('-- Validator --', JSON.stringify({
modelVal:modelVal,
viewVal:viewVal,
ngModel: {
viewVal: $ngModel.$viewValue,
modelVal: $ngModel.$modelValue
}
}, null, 2))
return true;
};
$ngModel.$parsers.unshift(function(inputVal) {
console.log('------ VIEW VALUE CHANGED (user typed in input)------');
console.log('-- Parser --', JSON.stringify({
inputVal:inputVal,
ngModel: {
viewVal: $ngModel.$viewValue,
modelVal: $ngModel.$modelValue
}
}, null, 2))
return inputVal;
});
$ngModel.$viewChangeListeners.push(function() {
console.log('-- viewChangeListener --', JSON.stringify({
ngModel: {
viewVal: $ngModel.$viewValue,
modelVal: $ngModel.$modelValue
}
}, null, 2))
});
// same as $watch('foo')
$scope.$watch(function() {
return $ngModel.$viewValue;
}, function(newVal) {
console.log('-- $watch "foo" --', JSON.stringify({
newVal:newVal,
ngModel: {
viewVal: $ngModel.$viewValue,
modelVal: $ngModel.$modelValue
}
}, null, 2))
});
}
};
return directive;
})
;