angular: Form statusChanges with async validators doesn't leave PENDING state
🐞 bug report
Affected Package
The issue is caused by package @angular/formsIs this a regression?
No this has been around since early 2017 but version 11 claims to have fixed this, and yet the bug is still present.Description
FormGroup and FormControl do not emit first statusChanges when async validators are present, leaving the form in the PENDING state. Issues #14542 and #20424 reported this problem and were closed by #38354, which is available in Angular 11. However, running both of the stackblitz examples from these issues in Angular 11 demonstrates the original problem still exists.🔬 Minimal Reproduction
https://stackblitz.com/edit/angular-async-status-change🌍 Your Environment
Angular Version:
Angular CLI: 11.2.8
Node: 14.15.4
OS: darwin x64
Angular: 11.2.9
... animations, common, compiler, compiler-cli, core, forms
... platform-browser, platform-browser-dynamic, router
Ivy Workspace: Yes
Package Version
---------------------------------------------------------
@angular-devkit/architect 0.1102.8
@angular-devkit/build-angular 0.1102.8
@angular-devkit/core 11.2.8
@angular-devkit/schematics 11.2.8
@angular/cli 11.2.8
@schematics/angular 11.2.8
@schematics/update 0.1102.8
rxjs 6.6.7
typescript 4.1.5
Anything else relevant?
About this issue
- Original URL
- State: open
- Created 3 years ago
- Reactions: 41
- Comments: 43 (7 by maintainers)
Commits related to this issue
- fix(forms): Allow async validators to emit status change if a async validator that should have emitted execution was cancelled by a non-emitting validator execution. This issue can happen when a `For... — committed to JeanMeche/angular by JeanMeche 3 months ago
- fix(forms): Allow async validators to emit status change if a async validator that should have emitted execution was cancelled by a non-emitting validator execution. This issue can happen when a `For... — committed to JeanMeche/angular by JeanMeche 3 months ago
- fix(forms): Allow async validators to emit status change if a async validator that should have emitted execution was cancelled by a non-emitting validator execution. This issue can happen when a `For... — committed to JeanMeche/angular by JeanMeche 3 months ago
- fix(forms): Allow async validators to emit status change if a async validator that should have emitted execution was cancelled by a non-emitting validator execution. This issue can happen when a `For... — committed to JeanMeche/angular by JeanMeche 3 months ago
- fix(forms): Allow async validators to emit status change if a async validator that should have emitted execution was cancelled by a non-emitting validator execution. This issue can happen when a `For... — committed to JeanMeche/angular by JeanMeche 3 months ago
- fix(forms): Allow async validators to emit status change if a async validator that should have emitted execution was cancelled by a non-emitting validator execution. This issue can happen when a `For... — committed to JeanMeche/angular by JeanMeche 3 months ago
- fix(forms): Allow async validators to emit status change if a async validator that should have emitted execution was cancelled by a non-emitting validator execution. This issue can happen when a `For... — committed to JeanMeche/angular by JeanMeche 3 months ago
- fix(forms): Allow async validators to emit status change if a async validator that should have emitted execution was cancelled by a non-emitting validator execution. This issue can happen when a `For... — committed to JeanMeche/angular by JeanMeche 3 months ago
- fix(forms): Allow async validators to emit status change if a async validator that should have emitted execution was cancelled by a non-emitting validator execution. This issue can happen when a `For... — committed to JeanMeche/angular by JeanMeche 3 months ago
- fix(forms): Allow async validators to emit status change if a async validator that should have emitted execution was cancelled by a non-emitting validator execution. This issue can happen when a `For... — committed to JeanMeche/angular by JeanMeche 3 months ago
Plus
Still
As a hacky workaround you can manually trigger the validation after form initialization:
Hi @AndrewKushnir
In my case I am validating a FormGroup with an AsyncValidator against a backend endpoint. The form unfortunately stays in pending state, which makes the
form.valid
check unusable.There have been several people posting potential workarounds, as for my case none of these seems to be working. I guess the release of Angular 17 is too close for such a breaking change to come in. However there are several issues reported and from what I could find one that should have fixed this but didn’t, in an early major release of Angular (I think v11).
Could you guys at least provide an official way to work around this issue until it lands in a major release?
That would help very much. Thanks!
I’m eyeing at a fix with #55134. We’ll need to run some tests to see how breaking that change would be. If it’s too breaking, it might be available only via the new observable that will land with #54579.
Solve it like this
@AndrewKushnir I think it’s due to call of
_updateTreeValidity
withemitEvent
parameter asfalse
within_updateDomValue
. Maybe_updateTreeValidity
should evaluateemitEvent
property for each control by look whether it, has an asyncValidator, as it’s done in constructor of FormControl?OK angular team… Several years and we DO NOT HAVE even working async validators in CVA… and Validator interface.
NICE. At least, we have deprecated class based things and signals()…
Hard to believe.
Okay sorry you are right @tadamczak. Seems like I’m having another problem in my project. Here in my stackblitz the async validation works like a charm.
@spock123
@DmitryEfimenko My particular issue is that I’m trying to reuse
statusChanges
of an inner form where I have async validators when using the Composite ControlValueAccessor pattern, so that I don’t have to pass down the errors to the children (very cumbersome).So, I’m stuck with this code which doesn’t work in my CVA:
The only solution which came to mind was to do some sort of “polling” via
timeout
/retry
orswitchMap
/timer
, which works but the code is pretty ugly either way, eg:But it could be abstracted:
Hope this is helpful! (test that code cause I wrote it in 10 minutes, but it seems to be working)
PS. While this kinda works for my usecase I’m getting ExpressionChangedAfterItHasBeenChecked, I’ll see if there’s a solution for that 😃