fix(forms): async validator cancels previous subscription when input has changed (#13222)
Fixes #12709 Fixes #9120 Fixes #10074 Fixes #8923 PR Close #13222
This commit is contained in:
parent
22058298d3
commit
6c7300c7de
|
@ -384,6 +384,7 @@ export abstract class AbstractControl {
|
|||
this._updateValue();
|
||||
|
||||
if (this.enabled) {
|
||||
this._cancelExistingSubscription();
|
||||
this._errors = this._runValidator();
|
||||
this._status = this._calculateStatus();
|
||||
|
||||
|
@ -417,7 +418,6 @@ export abstract class AbstractControl {
|
|||
private _runAsyncValidator(emitEvent: boolean): void {
|
||||
if (this.asyncValidator) {
|
||||
this._status = PENDING;
|
||||
this._cancelExistingSubscription();
|
||||
const obs = toObservable(this.asyncValidator(this));
|
||||
this._asyncValidationSubscription =
|
||||
obs.subscribe({next: (res: {[key: string]: any}) => this.setErrors(res, {emitEvent})});
|
||||
|
|
|
@ -1582,6 +1582,29 @@ export function main() {
|
|||
expect(form.valid).toEqual(true);
|
||||
}));
|
||||
|
||||
it('async validator should not override result of sync validator', fakeAsync(() => {
|
||||
const fixture = initTest(FormGroupComp);
|
||||
const control =
|
||||
new FormControl('', Validators.required, uniqLoginAsyncValidator('expected', 100));
|
||||
fixture.componentInstance.form = new FormGroup({'login': control});
|
||||
fixture.detectChanges();
|
||||
tick();
|
||||
|
||||
expect(control.hasError('required')).toEqual(true);
|
||||
|
||||
const input = fixture.debugElement.query(By.css('input'));
|
||||
input.nativeElement.value = 'expected';
|
||||
dispatchEvent(input.nativeElement, 'input');
|
||||
|
||||
expect(control.pending).toEqual(true);
|
||||
|
||||
input.nativeElement.value = '';
|
||||
dispatchEvent(input.nativeElement, 'input');
|
||||
tick(110);
|
||||
|
||||
expect(control.valid).toEqual(false);
|
||||
}));
|
||||
|
||||
});
|
||||
|
||||
describe('errors', () => {
|
||||
|
@ -1829,12 +1852,12 @@ class MyInput implements ControlValueAccessor {
|
|||
dispatchChangeEvent() { this.onInput.emit(this.value.substring(1, this.value.length - 1)); }
|
||||
}
|
||||
|
||||
function uniqLoginAsyncValidator(expectedValue: string) {
|
||||
function uniqLoginAsyncValidator(expectedValue: string, timeout: number = 0) {
|
||||
return (c: AbstractControl) => {
|
||||
let resolve: (result: any) => void;
|
||||
const promise = new Promise(res => { resolve = res; });
|
||||
const res = (c.value == expectedValue) ? null : {'uniqLogin': true};
|
||||
resolve(res);
|
||||
setTimeout(() => resolve(res), timeout);
|
||||
return promise;
|
||||
};
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue