From 7180ec9e7c3e33c1acfba1f40190d2de804c062e Mon Sep 17 00:00:00 2001 From: Dylan Hunn Date: Thu, 10 Jun 2021 14:41:29 -0700 Subject: [PATCH] fix(forms): changes to status not always being emitted to statusChanges observable for async validators. (#42553) When a FormControl, FormArray, or FormGroup is first constructed, if an async validator is attached, the `statusChanges` observable should receive a message when the validator complete (i.e. pending -> valid/invalid). If the validator was provided as part of the constructor options, it was not fired at construction time, which is fixed in this PR. Fixes #35309. PR Close #42553 --- packages/forms/src/model.ts | 6 +++--- packages/forms/test/form_array_spec.ts | 17 +++++++++++++++++ packages/forms/test/form_control_spec.ts | 16 ++++++++++++++++ packages/forms/test/form_group_spec.ts | 15 +++++++++++++++ 4 files changed, 51 insertions(+), 3 deletions(-) diff --git a/packages/forms/src/model.ts b/packages/forms/src/model.ts index ae6550a82b..96c8f320fd 100644 --- a/packages/forms/src/model.ts +++ b/packages/forms/src/model.ts @@ -1167,7 +1167,7 @@ export class FormControl extends AbstractControl { // `VALID` or `INVALID`. // The status should be broadcasted via the `statusChanges` observable, so we set `emitEvent` // to `true` to allow that during the control creation process. - emitEvent: !!asyncValidator + emitEvent: !!this.asyncValidator }); } @@ -1433,7 +1433,7 @@ export class FormGroup extends AbstractControl { // If `asyncValidator` is present, it will trigger control status change from `PENDING` to // `VALID` or `INVALID`. The status should be broadcasted via the `statusChanges` observable, // so we set `emitEvent` to `true` to allow that during the control creation process. - emitEvent: !!asyncValidator + emitEvent: !!this.asyncValidator }); } @@ -1887,7 +1887,7 @@ export class FormArray extends AbstractControl { // `VALID` or `INVALID`. // The status should be broadcasted via the `statusChanges` observable, so we set `emitEvent` // to `true` to allow that during the control creation process. - emitEvent: !!asyncValidator + emitEvent: !!this.asyncValidator }); } diff --git a/packages/forms/test/form_array_spec.ts b/packages/forms/test/form_array_spec.ts index 5097be6621..0ddc42a564 100644 --- a/packages/forms/test/form_array_spec.ts +++ b/packages/forms/test/form_array_spec.ts @@ -1055,6 +1055,23 @@ describe('FormArray', () => { expect(g.errors).toEqual({'async': true, 'other': true}); expect(g.pending).toEqual(false); })); + + it('should fire statusChanges events when async validators are added via options object', + fakeAsync(() => { + // The behavior is tested (in other spec files) for each of the model types (`FormControl`, + // `FormGroup` and `FormArray`). + let statuses: string[] = []; + + // Create a form control with an async validator added via options object. + const asc = new FormArray([], {asyncValidators: [() => Promise.resolve(null)]}); + + // Subscribe to status changes. + asc.statusChanges.subscribe((status: any) => statuses.push(status)); + + // After a tick, the async validator should change status PENDING -> VALID. + tick(); + expect(statuses).toEqual(['VALID']); + })); }); describe('disable() & enable()', () => { diff --git a/packages/forms/test/form_control_spec.ts b/packages/forms/test/form_control_spec.ts index d4344e8ce5..3584e963f1 100644 --- a/packages/forms/test/form_control_spec.ts +++ b/packages/forms/test/form_control_spec.ts @@ -879,6 +879,22 @@ describe('FormControl', () => { tick(); })); + it('should fire statusChanges events for async validators added via options object', + fakeAsync(() => { + // The behavior can be tested for each of the model types. + let statuses: string[] = []; + + // Create a form control with an async validator added via options object. + const asc = new FormControl('', {asyncValidators: [() => Promise.resolve(null)]}); + + // Subscribe to status changes. + asc.statusChanges.subscribe((status: any) => statuses.push(status)); + + // After a tick, the async validator should change status PENDING -> VALID. + tick(); + expect(statuses).toEqual(['VALID']); + })); + it('should fire an event after the status has been updated to pending', fakeAsync(() => { const c = new FormControl('old', Validators.required, asyncValidator('expected')); diff --git a/packages/forms/test/form_group_spec.ts b/packages/forms/test/form_group_spec.ts index 830a3ae587..37de069850 100644 --- a/packages/forms/test/form_group_spec.ts +++ b/packages/forms/test/form_group_spec.ts @@ -819,6 +819,21 @@ describe('FormGroup', () => { group = new FormGroup({'one': control}); })); + it('should fire statusChanges events for async validators added via options object', + fakeAsync(() => { + // The behavior can be tested for each of the model types. + let statuses: string[] = []; + + // Create a form control with an async validator added via options object. + const asc = new FormGroup({}, {asyncValidators: [() => Promise.resolve(null)]}); + + // Subscribe to status changes. + asc.statusChanges.subscribe((status: any) => statuses.push(status)); + + // After a tick, the async validator should change status PENDING -> VALID. + tick(); + expect(statuses).toEqual(['VALID']); + })); // TODO(kara): update these tests to use fake Async it('should fire a statusChange if child has async validation change', done => {