-
Notifications
You must be signed in to change notification settings - Fork 27.4k
$asyncValidators run multiple times per element on load (given ng-repeat || ng-(min/max)length)) #14691
Comments
that's expected, I'm pretty sure. that's how the digest cycle works. it needs to make sure that the value hasn't changed and is 'stable'. you could make your async validator check for a presence of a viewValue on the model, so it would return only when it's present, and reject the validation right away if it doesn't |
That would have been an acceptable workaround, but in this scenario, viewValue is already present when the asyncValidator fires its (extra) requests. The only workaround I suspect works is to package $asyncValidator in the same directive along with all of the other directive's functionality (minlength, maxlength, required) that I want to use. |
I wouldn't quite say it's expected behavior, since it works as expected without ngRepeat. There are two issues that play together:
This is honestly pretty weird, because I would expect ngModel to be validated once after the first $digest without ngRepeat, and it's never validated. With ngRepeat, it's validated once and 1 time per validation directive. Both cases are wrong, imo, but I suspect the first case doesn't appear too often, since on load usually more than one digest happens. |
+1 |
I am running into exactly the same problem. In fact, I only want all my validators to only fire when I changed the value and not right away. Is there a way for this? Say, I tried adding ngModel.$touched check in the directive, but then I could not fire it at all. What is the best workaround to only fire validations when the value is changed? I am using AngularJs 1.6.4 and seeing this behavior in Google Chrome |
I am having the same issue any update on this issue? or any workaround? I am using angular 1.5.11 and I have my async validator with Regards |
Hi, I think I was able to prevent running it too many times on load but I'm not 100% certain as I then found that it was not running all the times when I needed, so I made few extra changes lately. I plan to write a TechNet article that explains my solution in all the details, I'll post the link once I do that. |
@NaomiN +1 |
I just posted that article - will appreciate questions and suggestions to improve |
I solved(?) my issue with the following approach:
|
@ha0x7c7 Just to mention,
|
@adubadzel I didn't know about that feature back then, but I should mention that I didn't cache the resolved result, I cached the promise and not the value. |
@ha0x7c7, Thanks for reply, now I see that our solutions work differently and can be used for different use cases. |
While workarounds exist, I think this is an interesting issue, and tried to get to the bottom of it.
In normal circumstances, $compile sets up the observers, which are also fired on init. However, since the modelValue isn't set up yet, $validate doesn't do anything. Then, the ngModelWatch runs, and sets the modelValue, and calls $validate itself. When watch+transclude comes into the mix, the whole process is reversed, so that when the observers are initially called, the modelValue is set, and $validate is called again. However, the whole problem also happens without ngRepeat if you use interpolation for one of the minlength etc. directives, see: plnkr.co/edit/b4tY6u4u7o6QJJ7scBpD?p=preview I think we can fix this for the built-in validators by storing the initial attribute value, and only calling validate if the the observed new value is actually different. |
This commit updates in-built validators with observers to prevent multiple calls to $validate that could happen after compilation in certain circumstances: - when an input is wrapped in a transclude: element directive (e.g. ngRepeat), the order of execution between ngModel and the input / validation directives changes so that the initial observer call happens when ngModel has already been initalized, leading to another call to $validate, which calls *all* defined validators again. Without ngRepeat, ngModel hasn't been initialized yet, and $validate does not call the validators. When using validators with scope expressions, the expression value is not available when ngModel first runs the validators (e.g. ngMinlength="myMinlength"). Only in the first call to the observer does the value become available, making a call to $validate a necessity. This commit solves the first problem by storing the validation attribute value so we can compare the current value and the observed value - which will be the same after compilation. The second problem is solved by parsing the validation expression once in the link function, so the value is available when ngModel first validates. Closes angular#14691
This commit updates in-built validators with observers to prevent multiple calls to $validate that could happen after compilation in certain circumstances: - when an input is wrapped in a transclude: element directive (e.g. ngRepeat), the order of execution between ngModel and the input / validation directives changes so that the initial observer call happens when ngModel has already been initalized, leading to another call to $validate, which calls *all* defined validators again. Without ngRepeat, ngModel hasn't been initialized yet, and $validate does not call the validators. When using validators with scope expressions, the expression value is not available when ngModel first runs the validators (e.g. ngMinlength="myMinlength"). Only in the first call to the observer does the value become available, making a call to $validate a necessity. This commit solves the first problem by storing the validation attribute value so we can compare the current value and the observed value - which will be the same after compilation. The second problem is solved by parsing the validation expression once in the link function, so the value is available when ngModel first validates. Closes angular#14691
This commit updates in-built validators with observers to prevent multiple calls to $validate that could happen after compilation in certain circumstances: - when an input is wrapped in a transclude: element directive (e.g. ngRepeat), the order of execution between ngModel and the input / validation directives changes so that the initial observer call happens when ngModel has already been initalized, leading to another call to $validate, which calls *all* defined validators again. Without ngRepeat, ngModel hasn't been initialized yet, and $validate does not call the validators. When using validators with scope expressions, the expression value is not available when ngModel first runs the validators (e.g. ngMinlength="myMinlength"). Only in the first call to the observer does the value become available, making a call to $validate a necessity. This commit solves the first problem by storing the validation attribute value so we can compare the current value and the observed value - which will be the same after compilation. The second problem is solved by parsing the validation expression once in the link function, so the value is available when ngModel first validates. Closes angular#14691
This commit updates in-built validators with observers to prevent multiple calls to $validate that could happen after compilation in certain circumstances: - when an input is wrapped in a transclude: element directive (e.g. ngRepeat), the order of execution between ngModel and the input / validation directives changes so that the initial observer call happens when ngModel has already been initalized, leading to another call to $validate, which calls *all* defined validators again. Without ngRepeat, ngModel hasn't been initialized yet, and $validate does not call the validators. When using validators with scope expressions, the expression value is not available when ngModel first runs the validators (e.g. ngMinlength="myMinlength"). Only in the first call to the observer does the value become available, making a call to $validate a necessity. This commit solves the first problem by storing the validation attribute value so we can compare the current value and the observed value - which will be the same after compilation. The second problem is solved by parsing the validation expression once in the link function, so the value is available when ngModel first validates. Closes angular#14691
This commit updates in-built validators with observers to prevent multiple calls to $validate that could happen on initial linking of the directives in certain circumstances: - when an input is wrapped in a transclude: element directive (e.g. ngRepeat), the order of execution between ngModel and the input / validation directives changes so that the initial observer call happens when ngModel has already been initalized, leading to another call to $validate, which calls *all* defined validators again. Without ngRepeat, ngModel hasn't been initialized yet, and $validate does not call the validators. When using validators with scope expressions, the expression value is not available when ngModel first runs the validators (e.g. ngMinlength="myMinlength"). Only in the first call to the observer does the value become available, making a call to $validate a necessity. This commit solves the first problem by storing the validation attribute value so we can compare the current value and the observed value - which will be the same after compilation. The second problem is solved by parsing the validation expression once in the link function, so the value is available when ngModel first validates. Closes #14691 Closes #16760
This commit updates in-built validators with observers to prevent multiple calls to $validate that could happen on initial linking of the directives in certain circumstances: - when an input is wrapped in a transclude: element directive (e.g. ngRepeat), the order of execution between ngModel and the input / validation directives changes so that the initial observer call happens when ngModel has already been initalized, leading to another call to $validate, which calls *all* defined validators again. Without ngRepeat, ngModel hasn't been initialized yet, and $validate does not call the validators. When using validators with scope expressions, the expression value is not available when ngModel first runs the validators (e.g. ngMinlength="myMinlength"). Only in the first call to the observer does the value become available, making a call to $validate a necessity. This commit solves the first problem by storing the validation attribute value so we can compare the current value and the observed value - which will be the same after compilation. The second problem is solved by parsing the validation expression once in the link function, so the value is available when ngModel first validates. Closes #14691 Closes #16760
Note: for support questions, please use one of these channels: https://github.com/angular/angular.js/blob/master/CONTRIBUTING.md#question. This repository's issues are reserved for feature requests and bug reports.
Do you want to request a feature or report a bug?
Bug
What is the current behavior?
If you have a model defined already and it's put into an input text element with a custom validator defined and this input field is in an ng-repeat, then, on the page load, the $ayncValidator will be run once, and then run in quick succession run again for every validator directive such as "minlength, maxlength, ng-maxlength, ng-minlength, required".
If the current behavior is a bug, please provide the steps to reproduce and if possible a minimal demo of the problem via https://plnkr.co or similar (template: http://plnkr.co/edit/tpl:yBpEi4).
http://jsfiddle.net/yofk7u0L/5/
What is the expected behavior?
$asyncValidators only fire once upon directive initialization with a non-empty model, regardless of how many other attribute directives that are validators exist in the element, also regardless of whether they're in ng-repeat or not.
What is the motivation / use case for changing the behavior?
In the application I'm currently working on, a user can have up to 10 of these $asyncValidator input fields loaded up at the same time. With minlength, maxlength, and required enabled on each, this makes 30 EXTRA requests to my API, on top of the 10 that were welcome. If I use ng-minlength and ng-maxlength, it could bump it up an extra twenty (two per input element)
Which versions of Angular, and which browser / OS are affected by this issue? Did this work in previous versions of Angular? Please also test with the latest stable and snapshot (https://code.angularjs.org/snapshot/) versions.
Firefox and Chrome on Angular 1.5.3, 1.5.5, 1.5.6 and v1.5.7-build.4838+sha.cfc8b41 exhibit this behavior.
I'm seeing this happen on both Windows 7 (1.5.3 & 1.5.5) and Linux Fedora 23 KDE (All the above)
Other information (e.g. stacktraces, related issues, suggestions how to fix)
Not sure how to fix this, but I guess:
The text was updated successfully, but these errors were encountered: