Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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