Angular typesafe reactive forms
import { FormGroup, FormControl, FormBuilder, AbstractControl } from '@angular/forms'; | |
export type FormGroupControlsOf<T> = { | |
[P in keyof T]: FormControl | FormGroup; | |
}; | |
export abstract class FormGroupTypeSafe<T> extends FormGroup { | |
//give the value a custom type s | |
value: T; | |
//create helper methods to achieve this syntax eg: this.form.getSafe(x => x.heroName).patchValue('Himan') | |
public abstract getSafe(propertyFunction: (typeVal: T) => any): AbstractControl; | |
public abstract setControlSafe(propertyFunction: (typeVal: T) => any, control: AbstractControl): void; | |
//If you need more function implement declare them here but implement them on FormBuilderTypeSafe.group instantiation. | |
} | |
export class FormControlTypeSafe<T> extends FormControl { | |
value: T; | |
} | |
export class FormBuilderTypeSafe extends FormBuilder { | |
//override group to be type safe | |
group<T>(controlsConfig: FormGroupControlsOf<T>, extra?: { | |
[key: string]: any; | |
} | null): FormGroupTypeSafe<T> {/*NOTE the return FormGroupTypeSafe<T> */ | |
//instantiate group from angular type | |
let gr = super.group(controlsConfig, extra) as FormGroupTypeSafe<T>; | |
let getPropertyName = (propertyFunction: Function): string => { | |
let properties: string[]; | |
if (propertyFunction.toString().indexOf('=>') !== -1) { | |
//propertyFunction.toString() sample value: | |
// x => x.hero.address.postcode | |
//we need the 'hero.address.postcode' | |
//for gr.get('hero.address.postcode') function | |
properties = propertyFunction.toString().split('=>')[1].trim() | |
.split('.') | |
.splice(1); | |
} else { | |
// https://github.com/dsherret/ts-nameof - helped me with the code below, THANX!!!! | |
//propertyFunction.toString() sample value: | |
// function(x) { return x.hero.address.postcode;} | |
//we need the 'hero.address.postcode' | |
//for gr.get('hero.address.postcode') function | |
properties = propertyFunction.toString() | |
.match(/(?![. ])([a-z0-9_]+)(?=[};.])/gi) | |
.splice(1); | |
} | |
return properties.join("."); | |
} | |
if (gr) { | |
//implement getSafe method | |
gr.getSafe = (propertyFunction: (typeVal: T) => any): AbstractControl => { | |
let getStr = getPropertyName(propertyFunction); | |
//console.log(getStr); | |
let p = gr.get(getStr) as FormGroupTypeSafe<T>; | |
return p; | |
} | |
//implement setControlSafe | |
gr.setControlSafe = (propertyFunction: (typeVal: T) => any, control: AbstractControl): void => { | |
let getStr = getPropertyName(propertyFunction); | |
//console.log(getStr); | |
gr.setControl(getStr, control); | |
} | |
//implement more functions as needed | |
} | |
return gr; | |
} | |
} |
This comment has been minimized.
This comment has been minimized.
I finally took the plunge and put this code into its own repository. Here is the |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
This comment has been minimized.
Yet another scenario - I need unit tests!
Scenario
Solution