Skip to content

Commit

Permalink
Merge pull request #139 from ntziolis/master
Browse files Browse the repository at this point in the history
feat(ControlsType): added typings for AbstractControls + FormArrays
  • Loading branch information
zak-cloudnc authored Feb 26, 2020
2 parents 05ad0a8 + 1472ab0 commit 00aea19
Show file tree
Hide file tree
Showing 4 changed files with 49 additions and 14 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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<T>`, `ControlsNames<T>`, `FormGroupOptions<T>`
- 7 interfaces: `Controls<T>`, `ControlsNames<T>`, `FormGroupOptions<T>`, `TypedFormGroup<T>`, `TypedFormArray<T>`, `TypedFormControl<T>`, `TypedAbstractControl<T>`
- 1 function: `subformComponentProviders`

So there's actually nothing to setup (like a module), you can just use them directly.
Expand Down
37 changes: 36 additions & 1 deletion projects/ngx-sub-form/src/lib/ngx-sub-form-utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand All @@ -19,7 +20,10 @@ export type ControlsNames<T> = { [K in keyof T]-?: K };

export type ControlMap<T, V> = { [K in keyof T]-?: V };

export type ControlsType<T> = { [K in keyof T]-?: T[K] extends any[] ? FormArray : AbstractControl };
export type ControlsType<T> = {
[K in keyof T]-?: T[K] extends any[] ? TypedFormArray<T[K]> : TypedFormControl<T[K]> | TypedFormGroup<T[K]>;
};

export type FormErrorsType<T> = {
[K in keyof T]-?: T[K] extends any[] ? (null | ValidationErrors)[] : ValidationErrors;
};
Expand All @@ -32,6 +36,37 @@ export type FormErrors<FormInterface> = null | Partial<
}
>;

// using set/patch value options signature from form controls to allow typing without additional casting
export interface TypedAbstractControl<TValue> extends AbstractControl {
value: TValue;
valueChanges: Observable<TValue>;
setValue(value: TValue, options?: Parameters<AbstractControl['setValue']>[1]): void;
patchValue(value: Partial<TValue>, options?: Parameters<AbstractControl['patchValue']>[1]): void;
}

export interface TypedFormGroup<TValue> extends FormGroup {
value: TValue;
valueChanges: Observable<TValue>;
controls: ControlsType<TValue>;
setValue(value: TValue, options?: Parameters<FormGroup['setValue']>[1]): void;
patchValue(value: Partial<TValue>, options?: Parameters<FormGroup['patchValue']>[1]): void;
}

export interface TypedFormArray<TValue extends any[]> extends FormArray {
value: TValue;
valueChanges: Observable<TValue>;
controls: TypedAbstractControl<TValue>[];
setValue(value: TValue, options?: Parameters<FormArray['setValue']>[1]): void;
patchValue(value: TValue, options?: Parameters<FormArray['patchValue']>[1]): void;
}

export interface TypedFormControl<TValue> extends FormGroup {
value: TValue;
valueChanges: Observable<TValue>;
setValue(value: TValue, options?: Parameters<FormControl['setValue']>[1]): void;
patchValue(value: Partial<TValue>, options?: Parameters<FormControl['patchValue']>[1]): void;
}

export type KeysWithType<T, V> = { [K in keyof T]: T[K] extends V ? K : never }[keyof T];

export type ArrayPropertyKey<T> = KeysWithType<T, Array<any>>;
Expand Down
17 changes: 11 additions & 6 deletions projects/ngx-sub-form/src/lib/ngx-sub-form.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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<FormInterface, MapValue> = (ctrl: AbstractControl, key: keyof FormInterface) => MapValue;
type MapControlFunction<FormInterface, MapValue> = (
ctrl: TypedAbstractControl<any>,
key: keyof FormInterface,
) => MapValue;
type FilterControlFunction<FormInterface> = (
ctrl: AbstractControl,
ctrl: TypedAbstractControl<any>,
key: keyof FormInterface,
isCtrlWithinFormArray: boolean,
) => boolean;
Expand All @@ -42,7 +47,7 @@ export abstract class NgxSubFormComponent<ControlInterface, FormInterface = Cont
return null as any;
}

return this.formGroup.controls as ControlsType<FormInterface>;
return (this.formGroup.controls as unknown) as ControlsType<FormInterface>;
}

public get formGroupValues(): Required<FormInterface> {
Expand Down Expand Up @@ -147,7 +152,7 @@ export abstract class NgxSubFormComponent<ControlInterface, FormInterface = Cont
return null;
}

const formControls: Controls<FormInterface> = this.formGroup.controls;
const formControls: ControlsType<FormInterface> = this.formGroup.controls;

const controls: Partial<ControlMap<FormInterface, MapValue | MapValue[]>> = {};

Expand Down Expand Up @@ -354,7 +359,7 @@ export abstract class NgxSubFormComponent<ControlInterface, FormInterface = Cont
const formControlNames: (keyof FormInterface)[] = Object.keys(this.formControlNames) as (keyof FormInterface)[];

const formValues: Observable<KeyValueForm>[] = 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 })),
),
Expand Down
7 changes: 1 addition & 6 deletions projects/ngx-sub-form/src/lib/ngx-sub-form.types.ts
Original file line number Diff line number Diff line change
@@ -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<FormInterface> {
Expand All @@ -12,11 +12,6 @@ type Nullable<T> = T | null;

export type NullableObject<T> = { [P in keyof T]: Nullable<T[P]> };

export interface TypedFormGroup<FormInterface> extends FormGroup {
controls: Controls<FormInterface>;
value: FormInterface;
}

export type TypedValidatorFn<T> = (formGroup: TypedFormGroup<T>) => ValidationErrors | null;

export type TypedAsyncValidatorFn<T> = (
Expand Down

0 comments on commit 00aea19

Please sign in to comment.