diff --git a/projects/ngx-sub-form/src/lib/ngx-sub-form.component.spec.ts b/projects/ngx-sub-form/src/lib/ngx-sub-form.component.spec.ts index 57a9f5ed..67d883f9 100644 --- a/projects/ngx-sub-form/src/lib/ngx-sub-form.component.spec.ts +++ b/projects/ngx-sub-form/src/lib/ngx-sub-form.component.spec.ts @@ -19,7 +19,7 @@ const getDefaultValues = (): Required => ({ }); class SubComponent extends NgxSubFormComponent { - getFormControls() { + protected getFormControls() { // even though optional, if we comment out color there should be a TS error return { color: new FormControl(getDefaultValues().color), @@ -32,6 +32,21 @@ class SubComponent extends NgxSubFormComponent { } } +describe(`Common`, () => { + it(`should call formGroup.updateValueAndValidity only if formGroup is defined`, (done: () => void) => { + const subComponent: SubComponent = new SubComponent(); + + const formGroupSpy = spyOn(subComponent.formGroup, 'updateValueAndValidity'); + + expect(formGroupSpy).not.toHaveBeenCalled(); + + setTimeout(() => { + expect(formGroupSpy).toHaveBeenCalled(); + done(); + }, 0); + }); +}); + describe(`NgxSubFormComponent`, () => { let subComponent: SubComponent; diff --git a/projects/ngx-sub-form/src/lib/ngx-sub-form.component.ts b/projects/ngx-sub-form/src/lib/ngx-sub-form.component.ts index 073a3e47..37840af0 100644 --- a/projects/ngx-sub-form/src/lib/ngx-sub-form.component.ts +++ b/projects/ngx-sub-form/src/lib/ngx-sub-form.component.ts @@ -7,11 +7,14 @@ import { ControlMap, Controls, ControlsNames } from './ngx-sub-form-utils'; export abstract class NgxSubFormComponent implements ControlValueAccessor, Validator, OnDestroy { public get formGroupControls(): ControlMap { - return this.mapControls(); + // @note form-group-undefined we need the as syntax here because we do not want to expose the fact that + // the form can be undefined, it's hanlded internally to contain an Angular bug + return this.mapControls() as ControlMap; } public get formGroupValues(): Required { - return this.mapControls(ctrl => ctrl.value); + // see @note form-group-undefined for as syntax + return this.mapControls(ctrl => ctrl.value) as Required; } public get formGroupErrors(): null | Partial> { @@ -20,7 +23,7 @@ export abstract class NgxSubFormComponent ctrl.invalid, ); - if (!Object.keys(errors).length) { + if (!errors || !Object.keys(errors).length) { return null; } @@ -28,9 +31,13 @@ export abstract class NgxSubFormComponent { - return this.mapControls((_, key) => key); + // see @note form-group-undefined for as syntax + return this.mapControls((_, key) => key) as ControlsNames; } + // when developing the lib it's a good idea to set the formGroup type + // to current + `| undefined` to catch a bunch of possible issues + // see @note form-group-undefined public formGroup: FormGroup & { controls: Controls } = new FormGroup(this.getFormControls()) as any; protected onChange: Function | undefined = undefined; @@ -48,14 +55,20 @@ export abstract class NgxSubFormComponent { - this.formGroup.updateValueAndValidity({ emitEvent: false }); + if (this.formGroup) { + this.formGroup.updateValueAndValidity({ emitEvent: false }); + } }, 0); } private mapControls>( mapControl?: (ctrl: Controls[keyof FormInterface], key: keyof FormInterface) => MapValue, filterControl: (ctrl: Controls[keyof FormInterface]) => boolean = () => true, - ): T { + ): T | null { + if (!this.formGroup) { + return null; + } + const formControls: Controls = this.formGroup.controls; if (!mapControl) { @@ -147,7 +160,7 @@ export abstract class NgxSubFormComponent { - if (this.onChange) { + if (this.onChange && this.formGroup) { this.onChange(this.transformFromFormGroup(this.formGroup.value)); } }, 0);