From 03a71fe9c41fc101d47a1d4eec40ba176579ad5c Mon Sep 17 00:00:00 2001 From: ntziolis Date: Mon, 1 Jun 2020 10:31:32 +0200 Subject: [PATCH] Merge pull request #139 from ntziolis/master (typings) feat(ControlsType): added typings for AbstractControls + FormArrays # Conflicts: # projects/ngx-sub-form/src/lib/ngx-sub-form-utils.ts # projects/ngx-sub-form/src/lib/ngx-sub-form.component.ts # projects/ngx-sub-form/src/lib/ngx-sub-form.types.ts --- README.md | 2 +- .../src/lib/ngx-root-form.component.ts | 2 +- .../src/lib/ngx-sub-form-utils.ts | 47 ++++++++++++++++++- .../src/lib/ngx-sub-form.component.ts | 22 ++++----- .../src/lib/ngx-sub-form.types.ts | 14 +++--- .../ngx-sub-form/src/lib/sub-form-group.ts | 2 + 6 files changed, 67 insertions(+), 22 deletions(-) diff --git a/README.md b/README.md index b0e277a9..e79239d1 100644 --- a/README.md +++ b/README.md @@ -44,7 +44,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-root-form.component.ts b/projects/ngx-sub-form/src/lib/ngx-root-form.component.ts index e7377103..00d2c690 100644 --- a/projects/ngx-sub-form/src/lib/ngx-root-form.component.ts +++ b/projects/ngx-sub-form/src/lib/ngx-root-form.component.ts @@ -42,7 +42,7 @@ export abstract class NgxRootFormComponent !isEqual(newValue, this.formGroup.value)), tap(newValue => { if (!isNullOrUndefined(newValue)) { - this.formGroup.patchValue(newValue); + this.formGroup.patchValue(newValue, undefined); } }), ) 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 ecd979fa..ea7b9c58 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 @@ -1,5 +1,14 @@ +import { + ControlValueAccessor, + NG_VALUE_ACCESSOR, + NG_VALIDATORS, + ValidationErrors, + FormControl, + FormArray, + AbstractControl, + FormGroup, +} from '@angular/forms'; import { forwardRef, OnDestroy, Provider } from '@angular/core'; -import { AbstractControl, FormArray, NG_VALIDATORS, NG_VALUE_ACCESSOR, ValidationErrors } from '@angular/forms'; import { Observable, Subject, timer } from 'rxjs'; import { debounce, takeUntil } from 'rxjs/operators'; @@ -12,7 +21,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; }; @@ -25,6 +37,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 efba7ef5..d7e6b028 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 @@ -1,4 +1,4 @@ -import { Input, OnDestroy, OnInit, OnChanges, SimpleChanges } from '@angular/core'; +import { Input, OnChanges, OnDestroy, SimpleChanges } from '@angular/core'; import { AbstractControl, AbstractControlOptions, @@ -19,13 +19,15 @@ import { ControlsType, FormErrors, isNullOrUndefined, + TypedAbstractControl, + TypedFormGroup, } from './ngx-sub-form-utils'; -import { FormGroupOptions, NgxFormWithArrayControls, TypedFormGroup } from './ngx-sub-form.types'; -import { SubFormGroup, patchFormControl, SubFormArray } from './sub-form-group'; +import { FormGroupOptions, NgxFormWithArrayControls, TypedSubFormGroup } from './ngx-sub-form.types'; +import { patchFormControl, SubFormArray, SubFormGroup } from './sub-form-group'; type MapControlFunction = (ctrl: AbstractControl, key: keyof FormInterface) => MapValue; type FilterControlFunction = ( - ctrl: AbstractControl, + ctrl: TypedAbstractControl, key: keyof FormInterface, isCtrlWithinFormArray: boolean, ) => boolean; @@ -39,7 +41,7 @@ export abstract class NgxSubFormComponent; + return (this.formGroup.controls as unknown) as ControlsType; } public get formGroupValues(): Required { @@ -78,7 +80,7 @@ export abstract class NgxSubFormComponent; + @Input('subForm') formGroup!: TypedSubFormGroup;// | SubFormArray; protected emitNullOnDestroy = true; protected emitInitialValueOnInit = true; @@ -91,13 +93,11 @@ export abstract class NgxSubFormComponent { this.formGroup.removeControl(key); }); @@ -217,7 +217,7 @@ export abstract class NgxSubFormComponent = this.formGroup.controls; + const formControls: ControlsType = this.formGroup.controls; const controls: Partial> = {}; 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 81c7661f..6b16548f 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,7 @@ 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, ControlsType } from './ngx-sub-form-utils'; +import { SubFormGroup } from './sub-form-group'; // @deprecated export interface OnFormUpdate { @@ -8,16 +9,10 @@ export interface OnFormUpdate { onFormUpdate?: (formUpdate: FormUpdate) => void; } -type Omit = Pick>; type Nullable = T | null; export type NullableObject = { [P in keyof T]: Nullable }; -export type TypedFormGroup = Omit & { - controls: Controls; - value: FormInterface; -}; - export type TypedValidatorFn = (formGroup: TypedFormGroup) => ValidationErrors | null; export type TypedAsyncValidatorFn = ( @@ -47,3 +42,8 @@ export interface FormGroupOptions { export interface NgxFormWithArrayControls { createFormArrayControl(key: ArrayPropertyKey, value: ArrayPropertyValue): FormControl; } + + +export interface TypedSubFormGroup extends SubFormGroup { + controls: ControlsType; +} \ No newline at end of file diff --git a/projects/ngx-sub-form/src/lib/sub-form-group.ts b/projects/ngx-sub-form/src/lib/sub-form-group.ts index d733d447..41be9e9f 100644 --- a/projects/ngx-sub-form/src/lib/sub-form-group.ts +++ b/projects/ngx-sub-form/src/lib/sub-form-group.ts @@ -102,6 +102,8 @@ export class SubFormGroup extends FormGroup { const transformedValue = (this.transformToFormGroup((value as unknown) as TControl, {}) as unknown) as TForm; + // TODO rethink as this might not work as we want it, we might not even need this anymore + // @ts-ignore (super.value as any) = transformedValue; this.controlValue = value;