Skip to content

Commit 03a71fe

Browse files
committed
Merge pull request cloudnc#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
1 parent 9ae9ba1 commit 03a71fe

File tree

6 files changed

+67
-22
lines changed

6 files changed

+67
-22
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ If you want to see the demo in action, please visit [https://cloudnc.github.io/n
4444

4545
- 2 classes for top level form components: `NgxRootFormComponent`, `NgxAutomaticRootFormComponent`
4646
- 2 classes for sub level form components: `NgxSubFormComponent`, `NgxSubFormRemapComponent`
47-
- 3 interfaces: `Controls<T>`, `ControlsNames<T>`, `FormGroupOptions<T>`
47+
- 7 interfaces: `Controls<T>`, `ControlsNames<T>`, `FormGroupOptions<T>`, `TypedFormGroup<T>`, `TypedFormArray<T>`, `TypedFormControl<T>`, `TypedAbstractControl<T>`
4848
- 1 function: `subformComponentProviders`
4949

5050
So there's actually nothing to setup (like a module), you can just use them directly.

projects/ngx-sub-form/src/lib/ngx-root-form.component.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ export abstract class NgxRootFormComponent<ControlInterface, FormInterface = Con
4242
filter(newValue => !isEqual(newValue, this.formGroup.value)),
4343
tap(newValue => {
4444
if (!isNullOrUndefined(newValue)) {
45-
this.formGroup.patchValue(newValue);
45+
this.formGroup.patchValue(newValue, undefined);
4646
}
4747
}),
4848
)

projects/ngx-sub-form/src/lib/ngx-sub-form-utils.ts

Lines changed: 45 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,14 @@
1+
import {
2+
ControlValueAccessor,
3+
NG_VALUE_ACCESSOR,
4+
NG_VALIDATORS,
5+
ValidationErrors,
6+
FormControl,
7+
FormArray,
8+
AbstractControl,
9+
FormGroup,
10+
} from '@angular/forms';
111
import { forwardRef, OnDestroy, Provider } from '@angular/core';
2-
import { AbstractControl, FormArray, NG_VALIDATORS, NG_VALUE_ACCESSOR, ValidationErrors } from '@angular/forms';
312
import { Observable, Subject, timer } from 'rxjs';
413
import { debounce, takeUntil } from 'rxjs/operators';
514

@@ -12,7 +21,10 @@ export type ControlsNames<T> = { [K in keyof T]-?: K };
1221

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

15-
export type ControlsType<T> = { [K in keyof T]-?: T[K] extends any[] ? FormArray : AbstractControl };
24+
export type ControlsType<T> = {
25+
[K in keyof T]-?: T[K] extends any[] ? TypedFormArray<T[K]> : TypedFormControl<T[K]> | TypedFormGroup<T[K]>;
26+
};
27+
1628
export type FormErrorsType<T> = {
1729
[K in keyof T]-?: T[K] extends any[] ? (null | ValidationErrors)[] : ValidationErrors;
1830
};
@@ -25,6 +37,37 @@ export type FormErrors<FormInterface> = null | Partial<
2537
}
2638
>;
2739

40+
// using set/patch value options signature from form controls to allow typing without additional casting
41+
export interface TypedAbstractControl<TValue> extends AbstractControl {
42+
value: TValue;
43+
valueChanges: Observable<TValue>;
44+
setValue(value: TValue, options?: Parameters<AbstractControl['setValue']>[1]): void;
45+
patchValue(value: Partial<TValue>, options?: Parameters<AbstractControl['patchValue']>[1]): void;
46+
}
47+
48+
export interface TypedFormGroup<TValue> extends FormGroup {
49+
value: TValue;
50+
valueChanges: Observable<TValue>;
51+
controls: ControlsType<TValue>;
52+
setValue(value: TValue, options?: Parameters<FormGroup['setValue']>[1]): void;
53+
patchValue(value: Partial<TValue>, options?: Parameters<FormGroup['patchValue']>[1]): void;
54+
}
55+
56+
export interface TypedFormArray<TValue extends any[]> extends FormArray {
57+
value: TValue;
58+
valueChanges: Observable<TValue>;
59+
controls: TypedAbstractControl<TValue>[];
60+
setValue(value: TValue, options?: Parameters<FormArray['setValue']>[1]): void;
61+
patchValue(value: TValue, options?: Parameters<FormArray['patchValue']>[1]): void;
62+
}
63+
64+
export interface TypedFormControl<TValue> extends FormGroup {
65+
value: TValue;
66+
valueChanges: Observable<TValue>;
67+
setValue(value: TValue, options?: Parameters<FormControl['setValue']>[1]): void;
68+
patchValue(value: Partial<TValue>, options?: Parameters<FormControl['patchValue']>[1]): void;
69+
}
70+
2871
export type KeysWithType<T, V> = { [K in keyof T]: T[K] extends V ? K : never }[keyof T];
2972

3073
export type ArrayPropertyKey<T> = KeysWithType<T, Array<any>>;

projects/ngx-sub-form/src/lib/ngx-sub-form.component.ts

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { Input, OnDestroy, OnInit, OnChanges, SimpleChanges } from '@angular/core';
1+
import { Input, OnChanges, OnDestroy, SimpleChanges } from '@angular/core';
22
import {
33
AbstractControl,
44
AbstractControlOptions,
@@ -19,13 +19,15 @@ import {
1919
ControlsType,
2020
FormErrors,
2121
isNullOrUndefined,
22+
TypedAbstractControl,
23+
TypedFormGroup,
2224
} from './ngx-sub-form-utils';
23-
import { FormGroupOptions, NgxFormWithArrayControls, TypedFormGroup } from './ngx-sub-form.types';
24-
import { SubFormGroup, patchFormControl, SubFormArray } from './sub-form-group';
25+
import { FormGroupOptions, NgxFormWithArrayControls, TypedSubFormGroup } from './ngx-sub-form.types';
26+
import { patchFormControl, SubFormArray, SubFormGroup } from './sub-form-group';
2527

2628
type MapControlFunction<FormInterface, MapValue> = (ctrl: AbstractControl, key: keyof FormInterface) => MapValue;
2729
type FilterControlFunction<FormInterface> = (
28-
ctrl: AbstractControl,
30+
ctrl: TypedAbstractControl<any>,
2931
key: keyof FormInterface,
3032
isCtrlWithinFormArray: boolean,
3133
) => boolean;
@@ -39,7 +41,7 @@ export abstract class NgxSubFormComponent<ControlInterface, FormInterface = Cont
3941
return null as any;
4042
}
4143

42-
return this.formGroup.controls as ControlsType<FormInterface>;
44+
return (this.formGroup.controls as unknown) as ControlsType<FormInterface>;
4345
}
4446

4547
public get formGroupValues(): Required<FormInterface> {
@@ -78,7 +80,7 @@ export abstract class NgxSubFormComponent<ControlInterface, FormInterface = Cont
7880
// see @note form-group-undefined
7981

8082
// tslint:disable-next-line: no-input-rename
81-
@Input('subForm') formGroup!: TypedFormGroup<FormInterface>;
83+
@Input('subForm') formGroup!: TypedSubFormGroup<ControlInterface, FormInterface>;// | SubFormArray<ControlInterface, FormInterface>;
8284

8385
protected emitNullOnDestroy = true;
8486
protected emitInitialValueOnInit = true;
@@ -91,13 +93,11 @@ export abstract class NgxSubFormComponent<ControlInterface, FormInterface = Cont
9193
return;
9294
}
9395

94-
// provide a descriptive error message when the declaration the parent for was incorrect
95-
if (!(this.formGroup instanceof SubFormGroup || this.formGroup instanceof SubFormArray)) {
96+
// TODO rethink if this can ever be a sub form array
97+
if (!(this.formGroup instanceof SubFormGroup)){// || this.formGroup instanceof SubFormArray)) {
9698
throw new Error('The subForm input needs to be of type SubFormGroup.');
9799
}
98100

99-
const oldControls = (this.formGroup.controls as unknown) as AbstractControl[];
100-
101101
Object.keys(this.formGroup.controls).forEach(key => {
102102
this.formGroup.removeControl(key);
103103
});
@@ -217,7 +217,7 @@ export abstract class NgxSubFormComponent<ControlInterface, FormInterface = Cont
217217
return null;
218218
}
219219

220-
const formControls: Controls<FormInterface> = this.formGroup.controls;
220+
const formControls: ControlsType<FormInterface> = this.formGroup.controls;
221221

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

projects/ngx-sub-form/src/lib/ngx-sub-form.types.ts

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,18 @@
11
import { FormControl, FormGroup, ValidationErrors } from '@angular/forms';
22
import { Observable } from 'rxjs';
3-
import { ArrayPropertyKey, ArrayPropertyValue, Controls, FormUpdate } from './ngx-sub-form-utils';
3+
import { ArrayPropertyKey, ArrayPropertyValue, Controls, FormUpdate, TypedFormGroup, ControlsType } from './ngx-sub-form-utils';
4+
import { SubFormGroup } from './sub-form-group';
45

56
// @deprecated
67
export interface OnFormUpdate<FormInterface> {
78
// @deprecated
89
onFormUpdate?: (formUpdate: FormUpdate<FormInterface>) => void;
910
}
1011

11-
type Omit<T, K> = Pick<T, Exclude<keyof T, K>>;
1212
type Nullable<T> = T | null;
1313

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

16-
export type TypedFormGroup<FormInterface> = Omit<FormGroup, 'controls' | 'value'> & {
17-
controls: Controls<FormInterface>;
18-
value: FormInterface;
19-
};
20-
2116
export type TypedValidatorFn<T> = (formGroup: TypedFormGroup<T>) => ValidationErrors | null;
2217

2318
export type TypedAsyncValidatorFn<T> = (
@@ -47,3 +42,8 @@ export interface FormGroupOptions<T> {
4742
export interface NgxFormWithArrayControls<T> {
4843
createFormArrayControl(key: ArrayPropertyKey<T>, value: ArrayPropertyValue<T>): FormControl;
4944
}
45+
46+
47+
export interface TypedSubFormGroup<TControl, TForm = TControl> extends SubFormGroup<TControl, TForm> {
48+
controls: ControlsType<TForm>;
49+
}

projects/ngx-sub-form/src/lib/sub-form-group.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,8 @@ export class SubFormGroup<TControl, TForm = TControl> extends FormGroup {
102102

103103
const transformedValue = (this.transformToFormGroup((value as unknown) as TControl, {}) as unknown) as TForm;
104104

105+
// TODO rethink as this might not work as we want it, we might not even need this anymore
106+
// @ts-ignore
105107
(super.value as any) = transformedValue;
106108

107109
this.controlValue = value;

0 commit comments

Comments
 (0)