I'm still quite new to AngularJS and I think I'm having trouble understanding the timing with $scope. In a controller, I've setup a watcher for some model data that is bound to various form elements. The watcher fires off an ajax request when the data changes, except if the form is not valid. I'm checking the form validity with myForm.$valid. This is all pretty straight forward, however, except when the model data is updated in the controller, and not the form. The validations runs as expected, but form.$valid still has the previous value, not what it should be with the updated data. For example, if the form was previously valid, then I delete the model value bound to a required input, the watcher will fire because the model data has changed, but when I log the value of myForm.$valid it's value is still true, even though it should be false.
我对AngularJS还是很陌生,我想我在理解$scope的时机上有困难。在控制器中,我为绑定到各种表单元素的一些模型数据设置了一个监视程序。当数据发生更改时,观察者将触发ajax请求,除非表单无效。我正在用我的表格检查表格的有效性。$valid。然而,这一切都是非常直接的,除非模型数据在控制器中更新,而不是在表单中更新。验证按预期运行,但是是表单。$valid仍然拥有先前的值,而不是更新的数据。例如,如果表单以前是有效的,那么我将删除绑定到必需输入的模型值,因为模型数据已经更改,但是当我记录myForm的值时,观察者将会触发。$valid它的值仍然为真,即使它应该是假的。
So my question is A. why is this happening?, but more importantly B. what is the correct way to handle what I am trying to accomplish? Would a custom directive make sense?
我的问题是,为什么会这样?,但更重要的是b。如何处理我想要完成的事情?定制指令有意义吗?
Here is a simplified example.
这里有一个简化的例子。
HTML:
HTML:
<div ng-controller="MyCtrl">
<form name="myForm">
<input type="text" name="myField" ng-model="myData" required>
<button type="button" ng-click="myData=''">Delete</button>
</form>
<div>
The watcher says the form is: <strong>{{ formStatus }}</strong>
</div>
</div>
Controller:
控制器:
myApp.controller('MyCtrl', ['$scope', function($scope) {
$scope.myData = 'abc';
$scope.formStatus = '';
$scope.$watch('myData', function(newVal, oldVal) {
if (newVal != oldVal) {
console.log("my data changed");
console.log("my form valid = ", $scope.myForm.$valid);
$scope.formStatus = $scope.myForm.$valid ? 'Valid' : 'Invalid';
}
});
}]);
Fiddle: http://jsfiddle.net/anpsince83/cK6cc/1/
小提琴:http://jsfiddle.net/anpsince83/cK6cc/1/
1 个解决方案
#1
3
A custom directive is the right way to go. It's generally recommended for a handful of reasons that watches be put in directives vs controllers. At a high level, one reason is that Angular recommends controllers be thin and only operate as the glue between the view and services. But you're hitting a specific, interesting reason.
定制指令是正确的方法。它通常被推荐的原因是,手表被放置在指令与控制器之间。在较高的层次上,一个原因是角推荐控制器很薄,并且只作为视图和服务之间的粘合剂。但是你碰到了一个特殊的,有趣的原因。
Controllers run before Angular directives are linked in. So your controller watch gets added to the watch list before Angular's ngModelWatch()
does for myData
. ngModelWatch()
is where Angular runs $formatters
(recalling that $formatters
, amongst other things, are where validation is triggered when a change happens to the model).
控制器在连接角指令之前运行。所以在角子的ngModelWatch()处理myData之前,您的控制器手表将被添加到监视列表中。ngModelWatch()是角运行$formatter的地方(回想一下,除了其他内容外,当模型发生更改时,还会触发验证)。
Since watches are executed in the order in which they were added to the watch list, your controller $watch
executes prior to validation being done by $formatters
.
由于手表是按照添加到监视列表的顺序执行的,所以您的控制器$watch在由$formatters执行验证之前执行。
If you instead put the same $watch
inside a directive it'll be added during link after ngModelWatch()
has been added to the watch list. Thus the directive's $watch
will execute after validation is done.
如果您将相同的$watch放在一个指令中,它将在ngModelWatch()被添加到监视列表之后的链接中添加。因此,指令的$watch将在验证完成后执行。
Here's an updated fiddle where you can watch the order of execution of $formatters
and each $watch
. And you'll see the directive version works as expected- running after $formatters
.
这里有一个更新的小提琴,你可以观看执行$formatters的顺序和每个$watch。你会看到指令版本的工作原理和预期一样——在格式化后运行。
#1
3
A custom directive is the right way to go. It's generally recommended for a handful of reasons that watches be put in directives vs controllers. At a high level, one reason is that Angular recommends controllers be thin and only operate as the glue between the view and services. But you're hitting a specific, interesting reason.
定制指令是正确的方法。它通常被推荐的原因是,手表被放置在指令与控制器之间。在较高的层次上,一个原因是角推荐控制器很薄,并且只作为视图和服务之间的粘合剂。但是你碰到了一个特殊的,有趣的原因。
Controllers run before Angular directives are linked in. So your controller watch gets added to the watch list before Angular's ngModelWatch()
does for myData
. ngModelWatch()
is where Angular runs $formatters
(recalling that $formatters
, amongst other things, are where validation is triggered when a change happens to the model).
控制器在连接角指令之前运行。所以在角子的ngModelWatch()处理myData之前,您的控制器手表将被添加到监视列表中。ngModelWatch()是角运行$formatter的地方(回想一下,除了其他内容外,当模型发生更改时,还会触发验证)。
Since watches are executed in the order in which they were added to the watch list, your controller $watch
executes prior to validation being done by $formatters
.
由于手表是按照添加到监视列表的顺序执行的,所以您的控制器$watch在由$formatters执行验证之前执行。
If you instead put the same $watch
inside a directive it'll be added during link after ngModelWatch()
has been added to the watch list. Thus the directive's $watch
will execute after validation is done.
如果您将相同的$watch放在一个指令中,它将在ngModelWatch()被添加到监视列表之后的链接中添加。因此,指令的$watch将在验证完成后执行。
Here's an updated fiddle where you can watch the order of execution of $formatters
and each $watch
. And you'll see the directive version works as expected- running after $formatters
.
这里有一个更新的小提琴,你可以观看执行$formatters的顺序和每个$watch。你会看到指令版本的工作原理和预期一样——在格式化后运行。