diff --git a/README.md b/README.md index e95da390..ff5e90b0 100644 --- a/README.md +++ b/README.md @@ -45,7 +45,7 @@ If you want to see the demo in action, please visit [https://cloudnc.github.io/n - 2 classes for top level form components: `NgxRootFormComponent`, `NgxAutomaticRootFormComponent` - 2 classes for sub level form components: `NgxSubFormComponent`, `NgxSubFormRemapComponent` -- 3 interfaces: `Controls`, `ControlsNames`, `FormGroupOptions` +- 7 interfaces: `Controls`, `ControlsNames`, `FormGroupOptions`, `TypedFormGroup`, `TypedFormArray`, `TypedFormControl`, `TypedAbstractControl` - 1 function: `subformComponentProviders` So there's actually nothing to setup (like a module), you can just use them directly. diff --git a/projects/ngx-sub-form/src/lib/ngx-sub-form-utils.ts b/projects/ngx-sub-form/src/lib/ngx-sub-form-utils.ts index 6f228615..8d5c116c 100644 --- a/projects/ngx-sub-form/src/lib/ngx-sub-form-utils.ts +++ b/projects/ngx-sub-form/src/lib/ngx-sub-form-utils.ts @@ -6,6 +6,7 @@ import { FormControl, FormArray, AbstractControl, + FormGroup, } from '@angular/forms'; import { InjectionToken, Type, forwardRef, OnDestroy } from '@angular/core'; import { Observable, Subject, timer } from 'rxjs'; @@ -19,7 +20,10 @@ export type ControlsNames = { [K in keyof T]-?: K }; export type ControlMap = { [K in keyof T]-?: V }; -export type ControlsType = { [K in keyof T]-?: T[K] extends any[] ? FormArray : AbstractControl }; +export type ControlsType = { + [K in keyof T]-?: T[K] extends any[] ? TypedFormArray : TypedFormControl | TypedFormGroup; +}; + export type FormErrorsType = { [K in keyof T]-?: T[K] extends any[] ? (null | ValidationErrors)[] : ValidationErrors; }; @@ -32,6 +36,37 @@ export type FormErrors = null | Partial< } >; +// using set/patch value options signature from form controls to allow typing without additional casting +export interface TypedAbstractControl extends AbstractControl { + value: TValue; + valueChanges: Observable; + setValue(value: TValue, options?: Parameters[1]): void; + patchValue(value: Partial, options?: Parameters[1]): void; +} + +export interface TypedFormGroup extends FormGroup { + value: TValue; + valueChanges: Observable; + controls: ControlsType; + setValue(value: TValue, options?: Parameters[1]): void; + patchValue(value: Partial, options?: Parameters[1]): void; +} + +export interface TypedFormArray extends FormArray { + value: TValue; + valueChanges: Observable; + controls: TypedAbstractControl[]; + setValue(value: TValue, options?: Parameters[1]): void; + patchValue(value: TValue, options?: Parameters[1]): void; +} + +export interface TypedFormControl extends FormGroup { + value: TValue; + valueChanges: Observable; + setValue(value: TValue, options?: Parameters[1]): void; + patchValue(value: Partial, options?: Parameters[1]): void; +} + export type KeysWithType = { [K in keyof T]: T[K] extends V ? K : never }[keyof T]; export type ArrayPropertyKey = KeysWithType>; 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 313386d3..e68f48d1 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 @@ -21,12 +21,17 @@ import { isNullOrUndefined, ControlsType, ArrayPropertyKey, + TypedAbstractControl, + TypedFormGroup, } from './ngx-sub-form-utils'; -import { FormGroupOptions, NgxFormWithArrayControls, OnFormUpdate, TypedFormGroup } from './ngx-sub-form.types'; +import { FormGroupOptions, NgxFormWithArrayControls, OnFormUpdate } from './ngx-sub-form.types'; -type MapControlFunction = (ctrl: AbstractControl, key: keyof FormInterface) => MapValue; +type MapControlFunction = ( + ctrl: TypedAbstractControl, + key: keyof FormInterface, +) => MapValue; type FilterControlFunction = ( - ctrl: AbstractControl, + ctrl: TypedAbstractControl, key: keyof FormInterface, isCtrlWithinFormArray: boolean, ) => boolean; @@ -42,7 +47,7 @@ export abstract class NgxSubFormComponent; + return (this.formGroup.controls as unknown) as ControlsType; } public get formGroupValues(): Required { @@ -147,7 +152,7 @@ export abstract class NgxSubFormComponent = this.formGroup.controls; + const formControls: ControlsType = this.formGroup.controls; const controls: Partial> = {}; @@ -354,7 +359,7 @@ export abstract class NgxSubFormComponent[] = formControlNames.map(key => - this.formGroup.controls[key].valueChanges.pipe( + ((this.formGroup.controls[key] as unknown) as AbstractControl).valueChanges.pipe( startWith(this.formGroup.controls[key].value), map(value => ({ key, value })), ), diff --git a/projects/ngx-sub-form/src/lib/ngx-sub-form.types.ts b/projects/ngx-sub-form/src/lib/ngx-sub-form.types.ts index 41882f49..8f881c3d 100644 --- a/projects/ngx-sub-form/src/lib/ngx-sub-form.types.ts +++ b/projects/ngx-sub-form/src/lib/ngx-sub-form.types.ts @@ -1,6 +1,6 @@ import { FormControl, FormGroup, ValidationErrors } from '@angular/forms'; import { Observable } from 'rxjs'; -import { ArrayPropertyKey, ArrayPropertyValue, Controls, FormUpdate } from './ngx-sub-form-utils'; +import { ArrayPropertyKey, ArrayPropertyValue, Controls, FormUpdate, TypedFormGroup } from './ngx-sub-form-utils'; // @deprecated export interface OnFormUpdate { @@ -12,11 +12,6 @@ type Nullable = T | null; export type NullableObject = { [P in keyof T]: Nullable }; -export interface TypedFormGroup extends FormGroup { - controls: Controls; - value: FormInterface; -} - export type TypedValidatorFn = (formGroup: TypedFormGroup) => ValidationErrors | null; export type TypedAsyncValidatorFn = (