Skip to content

Commit

Permalink
fix(forms): async validator cancels previous subscription when input …
Browse files Browse the repository at this point in the history
…has changed (#13222)

Fixes #12709
Fixes #9120
Fixes #10074
Fixes #8923

PR Close #13222
  • Loading branch information
Dzmitry Shylovich authored and mhevery committed Feb 3, 2017
1 parent bb71acc commit ec7f0fd
Show file tree
Hide file tree
Showing 2 changed files with 26 additions and 3 deletions.
2 changes: 1 addition & 1 deletion modules/@angular/forms/src/model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -384,6 +384,7 @@ export abstract class AbstractControl {
this._updateValue();

if (this.enabled) {
this._cancelExistingSubscription();
this._errors = this._runValidator();
this._status = this._calculateStatus();

Expand Down Expand Up @@ -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})});
Expand Down
27 changes: 25 additions & 2 deletions modules/@angular/forms/test/reactive_integration_spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1596,6 +1596,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', () => {
Expand Down Expand Up @@ -1843,12 +1866,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;
};
}
Expand Down

0 comments on commit ec7f0fd

Please sign in to comment.