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…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user