Skip to content

Instantly share code, notes, and snippets.

@wtho
Created December 8, 2019 23:57
Show Gist options
  • Save wtho/a31de2c79580ca5ea09d6e5fd74a2a3f to your computer and use it in GitHub Desktop.
Save wtho/a31de2c79580ca5ea09d6e5fd74a2a3f to your computer and use it in GitHub Desktop.
Typed Reactive Angular Forms
type Status = 'valid' | 'invalid';
@Component({
selector: 'app-component',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent {
profileForm = this.fb.group({
f1: [''],
f2: [3],
nested: this.tfb.group({
nf1: ['valid' as Status]
})
});
constructor(private fb: TypedFormBuilder) { }
}
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { AppComponent } from './app.component';
import { FormBuilder, ReactiveFormsModule } from '@angular/forms';
import { TypedFormBuilder } from './typed-form-builder';
@NgModule({
declarations: [
AppComponent
],
imports: [
BrowserModule,
ReactiveFormsModule
],
providers: [
{provide: TypedFormBuilder, useClass: FormBuilder}
],
bootstrap: [AppComponent],
})
export class AppModule { }
import {
FormControl,
AbstractControl,
AsyncValidatorFn,
ValidatorFn,
FormArray,
FormGroup
} from '@angular/forms';
import { Observable } from 'rxjs';
// tslint:disable: callable-types
declare type FormHooks = 'change' | 'blur' | 'submit';
export type Status = 'VALID' | 'INVALID' | 'PENDING' | 'DISABLED';
export declare interface TypedAbstractControlOptions<T> {
validators?: TypedValidatorFn<T> | TypedValidatorFn<T>[] | null;
asyncValidators?:
| TypedAsyncValidatorFn<T>
| TypedAsyncValidatorFn<T>[]
| null;
}
export declare abstract class TypedAbstractControl<T> {
validator: TypedValidatorFn<T> | null;
asyncValidator: TypedAsyncValidatorFn<T> | null;
readonly value: T;
constructor(
validator: TypedValidatorFn<T> | null,
asyncValidator: TypedAsyncValidatorFn<T> | null
);
readonly parent:
| TypedFormGroup<any>
| TypedFormArray<any>
| FormGroup
| FormArray;
readonly status: Status;
readonly valid: boolean;
readonly invalid: boolean;
readonly pending: boolean;
readonly disabled: boolean;
readonly enabled: boolean;
readonly errors: TypedValidationErrors<T> | null;
readonly pristine: boolean;
readonly dirty: boolean;
readonly touched: boolean;
readonly untouched: boolean;
readonly valueChanges: Observable<T>;
readonly statusChanges: Observable<Status>;
readonly updateOn: FormHooks;
setValidators(newValidator: ValidatorFn | ValidatorFn[] | null): void;
setAsyncValidators(
newValidator: AsyncValidatorFn | AsyncValidatorFn[] | null
): void;
clearValidators(): void;
clearAsyncValidators(): void;
markAsTouched(opts?: { onlySelf?: boolean }): void;
markAllAsTouched(): void;
markAsUntouched(opts?: { onlySelf?: boolean }): void;
markAsDirty(opts?: { onlySelf?: boolean }): void;
markAsPristine(opts?: { onlySelf?: boolean }): void;
markAsPending(opts?: { onlySelf?: boolean; emitEvent?: boolean }): void;
disable(opts?: { onlySelf?: boolean; emitEvent?: boolean }): void;
enable(opts?: { onlySelf?: boolean; emitEvent?: boolean }): void;
setParent(parent: FormGroup | FormArray): void;
abstract setValue(value: T, options?: Object): void;
abstract patchValue(value: T, options?: Object): void;
abstract reset(value?: T, options?: Object): void;
updateValueAndValidity(opts?: {
onlySelf?: boolean;
emitEvent?: boolean;
}): void;
setErrors(
errors: TypedValidationErrors<T> | null,
opts?: {
emitEvent?: boolean;
}
): void;
get<K extends keyof T>(path: K): TypedAbstractControl<T[K]> | null;
get<K extends keyof T, K2 extends keyof T[K]>(path: [K, K2]): TypedAbstractControl<T[K][K2]> | null;
get<K extends keyof T, K2 extends keyof T[K], K3 extends keyof T[K][K2]>(path: [K, K2, K3]): TypedAbstractControl<T[K][K2][K3]> | null;
getError(errorCode: string, path?: Array<string | number> | string): any;
hasError(errorCode: string, path?: Array<string | number> | string): boolean;
readonly root: TypedAbstractControl<any>;
}
export declare type TypedValidationErrors<T> = Partial<T>;
export declare interface TypedValidatorFn<T> extends ValidatorFn {
(control: TypedAbstractControl<T>): TypedValidationErrors<T> | null;
}
export declare interface TypedAsyncValidatorFn<T> extends AsyncValidatorFn {
(control: TypedAbstractControl<T>):
| Promise<TypedValidationErrors<T> | null>
| Observable<TypedValidationErrors<T> | null>;
}
type TypedValidator<T> = TypedValidatorFn<T> | TypedAsyncValidatorFn<T>;
type MaybeFormControl<T> =
| TypedFormControl<T>
| [T]
| [T, TypedValidator<T>]
| [T, TypedValidator<T>, TypedValidator<T>]
| [T, TypedValidator<T>, TypedValidator<T>, TypedValidator<T>]
| [T, TypedValidator<T>, TypedValidator<T>, TypedValidator<T>, TypedValidator<T>]
| TypedFormArray<T>
| TypedFormGroup<T>;
export declare class TypedFormControl<T> extends FormControl {
constructor(
formState?: T,
validatorOrOpts?:
| TypedValidatorFn<T>
| TypedValidatorFn<T>[]
| TypedAbstractControlOptions<T>
| null,
asyncValidator?:
| TypedAsyncValidatorFn<T>
| TypedAsyncValidatorFn<T>[]
| null
);
setValue(
value: T,
options?: {
onlySelf?: boolean;
emitEvent?: boolean;
emitModelToViewChange?: boolean;
emitViewToModelChange?: boolean;
}
): void;
patchValue(
value: T,
options?: {
onlySelf?: boolean;
emitEvent?: boolean;
emitModelToViewChange?: boolean;
emitViewToModelChange?: boolean;
}
): void;
reset(
formState?: T,
options?: {
onlySelf?: boolean;
emitEvent?: boolean;
}
): void;
}
export declare class TypedFormGroup<G> extends TypedAbstractControl<G> {
controls: {
[K in keyof G]: TypedAbstractControl<G[K]>;
};
constructor(
controls: {
[K in keyof G]: TypedAbstractControl<G[K]>;
},
validatorOrOpts?:
| TypedValidatorFn<G>
| TypedValidatorFn<G>[]
| TypedAbstractControlOptions<G>
| null,
asyncValidator?:
| TypedAsyncValidatorFn<G>
| TypedAsyncValidatorFn<G>[]
| null
);
registerControl<K extends keyof G>(
name: K,
control: TypedAbstractControl<G[K]>
): TypedAbstractControl<G[K]>;
addControl<K extends keyof G>(
name: K,
control: TypedAbstractControl<G[K]>
): void;
removeControl<K extends keyof G>(name: K): void;
setControl<K extends keyof G>(
name: K,
control: TypedAbstractControl<G[K]>
): void;
contains<K extends keyof G>(controlName: K): boolean;
setValue(
value: {
[K in keyof G]: G[K];
},
options?: {
onlySelf?: boolean;
emitEvent?: boolean;
}
): void;
patchValue(
value: {
[K in keyof G]: G[K];
},
options?: {
onlySelf?: boolean;
emitEvent?: boolean;
}
): void;
reset(
value?: G,
options?: {
onlySelf?: boolean;
emitEvent?: boolean;
}
): void;
getRawValue(): G;
}
export declare class TypedFormArray<T> extends AbstractControl {
controls: TypedAbstractControl<T>[];
constructor(
controls: TypedAbstractControl<T>[],
validatorOrOpts?:
| TypedValidatorFn<T>
| TypedValidatorFn<T>[]
| TypedAbstractControlOptions<T>
| null,
asyncValidator?:
| TypedAsyncValidatorFn<T>
| TypedAsyncValidatorFn<T>[]
| null
);
at(index: number): TypedAbstractControl<T>;
push(control: TypedAbstractControl<T>): void;
insert(index: number, control: TypedAbstractControl<T>): void;
removeAt(index: number): void;
setControl(index: number, control: TypedAbstractControl<T>): void;
setValue(
value: T[],
options?: {
onlySelf?: boolean;
emitEvent?: boolean;
}
): void;
patchValue(
value: T[],
options?: {
onlySelf?: boolean;
emitEvent?: boolean;
}
): void;
reset(
value?: T,
options?: {
onlySelf?: boolean;
emitEvent?: boolean;
}
): void;
getRawValue(): T[];
}
export abstract class TypedFormBuilder {
abstract group<T extends object>(
controlsConfig: {
[K in keyof T]: MaybeFormControl<T[K]>;
},
options?:
| TypedAbstractControlOptions<T>
| {
[K in keyof T]: T[K];
}
| null
): TypedFormGroup<T>;
abstract control<T>(
formState: T,
validatorOrOpts?:
| TypedValidatorFn<T>
| TypedValidatorFn<T>[]
| TypedAbstractControlOptions<T>
| null,
asyncValidator?:
| TypedAsyncValidatorFn<T>
| TypedAsyncValidatorFn<T>[]
| null
): TypedFormControl<T>;
abstract array<T>(
controlsConfig: T[],
validatorOrOpts?:
| TypedValidatorFn<T>
| TypedValidatorFn<T>[]
| TypedAbstractControlOptions<T>
| null,
asyncValidator?:
| TypedAsyncValidatorFn<T>
| TypedAsyncValidatorFn<T>[]
| null
): TypedFormArray<T>;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment