Skip to content

Instantly share code, notes, and snippets.

@rpbeukes
Last active August 1, 2020 19:03
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save rpbeukes/da1be9046b1eb5cb6aa38e20c607160b to your computer and use it in GitHub Desktop.
Save rpbeukes/da1be9046b1eb5cb6aa38e20c607160b to your computer and use it in GitHub Desktop.
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;
}
}
@rpbeukes
Copy link
Author

Yet another scenario - I need unit tests!

Scenario

function(x){cov_2imlqdpfhj.f[46]++;cov_2imlqdpfhj.s[149]++;return x.property;}

Solution

// https://github.com/dsherret/ts-nameof - helped me with the code below, THANX!!!!
//propertyFunction.toString() sample value:
//  function(x) { return x.hero.address.postcode;}
// OR
// eg function(x){cov_2imlqdpfhj.f[46]++;cov_2imlqdpfhj.s[149]++;return x.hero.address.postcode;}
//we need the 'hero.address.postcode'
//for gr.get('hero.address.postcode') function
properties = propertyFunction.toString()
    .match(/(return [;.a-zA-Z0-9 ]+)/gi)[0]
    .match(/(?![. ])([a-z0-9_]+)(?=[};.])/gi).splice(1);

@rpbeukes
Copy link
Author

I finally took the plunge and put this code into its own repository.
Contributions welcome - github/angular-typesafe-reactive-forms-helper

Here is the npm package: angular-typesafe-reactive-forms-helper.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment