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(); |     this._updateValue(); | ||||||
| 
 | 
 | ||||||
|     if (this.enabled) { |     if (this.enabled) { | ||||||
|  |       this._cancelExistingSubscription(); | ||||||
|       this._errors = this._runValidator(); |       this._errors = this._runValidator(); | ||||||
|       this._status = this._calculateStatus(); |       this._status = this._calculateStatus(); | ||||||
| 
 | 
 | ||||||
| @ -417,7 +418,6 @@ export abstract class AbstractControl { | |||||||
|   private _runAsyncValidator(emitEvent: boolean): void { |   private _runAsyncValidator(emitEvent: boolean): void { | ||||||
|     if (this.asyncValidator) { |     if (this.asyncValidator) { | ||||||
|       this._status = PENDING; |       this._status = PENDING; | ||||||
|       this._cancelExistingSubscription(); |  | ||||||
|       const obs = toObservable(this.asyncValidator(this)); |       const obs = toObservable(this.asyncValidator(this)); | ||||||
|       this._asyncValidationSubscription = |       this._asyncValidationSubscription = | ||||||
|           obs.subscribe({next: (res: {[key: string]: any}) => this.setErrors(res, {emitEvent})}); |           obs.subscribe({next: (res: {[key: string]: any}) => this.setErrors(res, {emitEvent})}); | ||||||
|  | |||||||
| @ -1582,6 +1582,29 @@ export function main() { | |||||||
|            expect(form.valid).toEqual(true); |            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', () => { |     describe('errors', () => { | ||||||
| @ -1829,12 +1852,12 @@ class MyInput implements ControlValueAccessor { | |||||||
|   dispatchChangeEvent() { this.onInput.emit(this.value.substring(1, this.value.length - 1)); } |   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) => { |   return (c: AbstractControl) => { | ||||||
|     let resolve: (result: any) => void; |     let resolve: (result: any) => void; | ||||||
|     const promise = new Promise(res => { resolve = res; }); |     const promise = new Promise(res => { resolve = res; }); | ||||||
|     const res = (c.value == expectedValue) ? null : {'uniqLogin': true}; |     const res = (c.value == expectedValue) ? null : {'uniqLogin': true}; | ||||||
|     resolve(res); |     setTimeout(() => resolve(res), timeout); | ||||||
|     return promise; |     return promise; | ||||||
|   }; |   }; | ||||||
| } | } | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user