-
Notifications
You must be signed in to change notification settings - Fork 27.1k
Form events with async validators doesn't emit StatusChangeEvent #56999
Description
Which @angular/* package(s) are the source of the bug?
forms
Is this a regression?
No
Description
In special cases emitting status does not still work. It was already reported in #41519 and should be fixed but it is not.
Problem is that Angular is internally calling _runAsyncValidator with own emitEvent flags which could override options like emitEvent which was previously called by using Forms API (pathValue, setValue etc.). Minimal reproduction is in Stackblitz link but there are several important things:
- There is asyncValidator with some delay
- There is formGroup and formControlName in template (without formControlName directive it will work)
- patchValue is called in @input setter - if you call it in ngOnInit it will work - because Angular is calling updateValueAndValidity several times this setup probably leads to scenarion described at the bottom
Actual implementation by @JeanMeche should fix this problem by passing this events into new events stream. I think that implementation is still problematic because the main problem is that something could internally change form status without informing subscribers, but despite this I believe that it still does not work as intended.
Also reichemn point out here #41519 (comment) that documentation is not correct.
In current implementation there is new internal flag shouldHaveEmitted which should have information about if some manual call of API request emitEvent, but problem is that it is based only on emitEvent flag and not also shouldHaveEmitted flag. So if there are multiple calls with updateValueAndValidity and async call with flag emitEvent is cancelled by them information about emit event is lost. If there is only one call with emitEvent false after manual call it is OK.
Therefore this scenario will break that behavior:
- Manual call which will trigger updateValueAndValidity -
form.patchValue({}, {emitEvent: true}) - Internal call which will trigger updateValueAndValidity with emitEvent false - this will cancel previous asyncValidatorSubscription and because emitEvent was true it will continue with shouldHaveEmitted set to true
- Internal call which will trigger updateValueAndValidity with emitEvent false - this will cancel previous asyncValidatorSubscription and because emitEvent was false it will continue with shouldHaveEmitted set to false
- Result is that previous information that events should be emitted are completly lost by internal calls of framework
It looks like this little change (when also shouldHaveEmitted is retrieved from canceled subscriptions) could fix this. I changed only these two lines
shouldHaveEmitted: shouldHaveEmitted !== false
and
const shouldHaveEmitted = (this._hasOwnPendingAsyncValidator?.emitEvent || this._hasOwnPendingAsyncValidator?.shouldHaveEmitted) || ?? false;
Code with context
_runAsyncValidator(shouldHaveEmitted, emitEvent) {
if (this.asyncValidator) {
this.status = PENDING;
this._hasOwnPendingAsyncValidator = {
emitEvent: emitEvent !== false,
shouldHaveEmitted: shouldHaveEmitted !== false
};
const obs = toObservable(this.asyncValidator(this));
this._asyncValidationSubscription = obs.subscribe((errors) => {
this._hasOwnPendingAsyncValidator = null;
this.setErrors(errors, {
emitEvent,
shouldHaveEmitted
});
});
}
}
_cancelExistingSubscription() {
if (this._asyncValidationSubscription) {
this._asyncValidationSubscription.unsubscribe();
const shouldHaveEmitted = (this._hasOwnPendingAsyncValidator?.emitEvent || this._hasOwnPendingAsyncValidator?.shouldHaveEmitted) || ?? false;
this._hasOwnPendingAsyncValidator = null;
return shouldHaveEmitted;
}
return false;
}
Please provide a link to a minimal reproduction of the bug
https://stackblitz.com/edit/stackblitz-starters-zssofu?file=src%2Fmain.ts
Please provide the exception or error you saw
No response
Please provide the environment you discovered this bug in (run ng version)
Angular CLI: 18.1.0
Node: 20.12.2
Package Manager: pnpm 9.2.0
OS: win32 x64
Angular: 18.1.0
... animations, cli, common, compiler, compiler-cli, core, forms
... platform-browser, platform-browser-dynamic, router
Package Version
---------------------------------------------------------
@angular-devkit/architect 0.1800.3
@angular-devkit/build-angular 18.1.0
@angular-devkit/core 18.0.3
@angular-devkit/schematics 18.0.3
@schematics/angular 18.0.3
rxjs 7.8.1
typescript 5.4.5
zone.js 0.14.7
Anything else?
No response