Use angular reactive forms with type-safety.
npm install angular-typesafe-reactive-forms-helperInstead of:
``typescript`
this.form.get('heroName').patchValue('He-Man');
angular-typesafe-reactive-forms-helper allows:
`typescript`
this.form.getSafe(x => x.heroName).patchValue('He-Man');
In order to make this work as closely as possible to the Angular way, an abstract class FormGroupTypeSafe was derived from Angular’s FormGroup with the intent not to break existing code.
Intellisense on FormGroupTypeSafe
!FormGroupTypeSafe.value Intellisense
Intellisense on FormGroupTypeSafe
!FormGroupTypeSafe.getSafe Intellisense
typescript
//interface used with FormGroupTypeSafe
interface IHeroFormModel {
name: string;
secretLairs: Array;
power: string;
sidekick: string
}
`$3
`typescript
/ TypeSafe Reactive Forms Changes /
//old code
//heroForm: FormGroup;
heroForm: FormGroupTypeSafe; `
$3
`typescript
constructor(
/ TypeSafe Reactive Forms Changes /
//old code - private fb: FormBuilder,
private fb: FormBuilderTypeSafe,
private heroService: HeroService) { this.createForm();
this.logNameChange();
}
`
$3
`typescript
// old code
// this.heroForm = this.fb.group({
// name: '',
// secretLairs: this.fb.array([]),
// power: '',
// sidekick: ''
// });
this.heroForm = this.fb.group({
name: new FormControl(''),
secretLairs: new FormControl([]),
power: new FormControl(''),
sidekick: new FormControl('')
});
// Nested type sample
interface IAddressModel {
suburb: string;
postcode: string;
}
interface ICustomerModel {
name: string;
address: IAddressModel;
}
this.form = this.fb.group({
name: new FormControl(null, [Validators.required]),
address: this.formBuilder.group({
suburb: new FormControl(''),
postcode: new FormControl('', [Validators.required]),
})
});
`
Peer Dependencies
@angular/forms and all its peer dependencies.This package has been tested with Angular 9, 10, 11.
(Should work with Angular 4, 5, 6, 7, 8 too)
I would encourage you to use versions Angular still support, see Angular's Support policy and schedule.
Blog
For a more in detail description of the benefits of this package, read my blog - Angular typesafe reactive forms.
When reading the blog, be mindful that it was written Oct-2017, before the
angular-typesafe-reactive-forms-helper package existed. Back then, the idea was to copy the code and adjust as needed. Since then, there were a few requests, thus angular-typesafe-reactive-forms-helper was born.Contributions
I only added features required by my projects, but I know more could be added with your help.
Create a PR to get the conversation started :smile:
Lastly
Use it…don’t use it :smile:
---
Release notes
The model used for all code samples:
`typescript
interface HeroFormModel {
heroName: string;
weapons: WeaponModel[];
}
interface WeaponModel {
name: string;
damagePoints: number;
}
`
$3
#### V2.0.2 (2021-05-18)
- Bump to Angular 11.
- Stop integration tests for Angular 8.
#### V2.0.1 (2020-12-09)
Package the correct library files, instead of the repository - rookie mistake :)
#### V2.0.0 (2020-11-06)
- use ng-packagr to fix bug - main.ts:15 Error: Angular JIT compilation failed
- add end-to-end-tests Angular 8, 9, 10
- removed Angular 7 integration tests from build pipeline as it is no longer supported by Angular team
New
dist file structure:`
./dist:
LICENSE
README.md
angular-typesafe-reactive-forms-helper.d.ts
angular-typesafe-reactive-forms-helper.metadata.json
bundles
esm2015
fesm2015
package.json
public_api.d.ts
src./dist/bundles:
angular-typesafe-reactive-forms-helper.umd.js
angular-typesafe-reactive-forms-helper.umd.js.map
angular-typesafe-reactive-forms-helper.umd.min.js
angular-typesafe-reactive-forms-helper.umd.min.js.map
./dist/esm2015:
angular-typesafe-reactive-forms-helper.js
public_api.js
src
./dist/esm2015/src:
angularTypesafeReactiveFormsHelper.js
getPropertyName.js
./dist/fesm2015:
angular-typesafe-reactive-forms-helper.js
angular-typesafe-reactive-forms-helper.js.map
./dist/src:
angularTypesafeReactiveFormsHelper.d.ts
getPropertyName.d.ts
`Old
dist file structure:`
./dist:
LICENSE
README.md
lib
package.json./lib:
angularTypesafeReactiveFormsHelper.d.ts
angularTypesafeReactiveFormsHelper.js
getPropertyName.d.ts
getPropertyName.js
`---
#### V1.8.2 (2020-09-04)
- Fix bug - getSafe() call fails and returns null when compiled to ES5.
---
#### V1.8.1 (2020-06-26)
- Bump to Angular 10.
- Stop integration tests for Angular 6.
---
#### V1.8.0 (2020-06-16)
- added
removeControlSafeSample:
`typescript
let sut: FormGroupTypeSafe = createGroup();
sut.removeControlSafe(x => x.heroName);
`The bottom code was avoided simply because in a variable rename scenario, the IDE should rename all the references instead of just informing one where the errors are.
`typescript
removeControl(name: keyof T): void;
removeControl(name: string): void;
`---
#### V1.7.0 (2020-05-14)
- added
controlsAngular's
forms.d.ts:
`typescript
controls: { [key: string]: AbstractControl; };
`angular-typesafe-reactive-forms-helper:
`typescript
controls: { [P in keyof T]: AbstractControlTypeSafe };
` Code samples:
`typescript
let sut: FormGroupTypeSafe = createGroup();
// $ExpectType { heroName: AbstractControlTypeSafe; weapons: AbstractControlTypeSafe; }
sut.controls; // $ExpectType AbstractControlTypeSafe
sut.controls.heroName;
// $ExpectType AbstractControlTypeSafe
sut.controls.weapons;
// $ExpectType string
sut.controls.heroName.value;
// $ExpectType WeaponModel[]
sut.controls.weapons.value;
`---
#### V1.6.0 (2020-04-22)
- added
statusChanges and status
Angular's forms.d.ts:
`typescript
/**
* The validation status of the control. There are four possible
* validation status values:
*
VALID: This control has passed all validation checks.
INVALID: This control has failed at least one validation check.
PENDING: This control is in the midst of conducting a validation check.
DISABLED: This control is exempt from validation checks.
*
* These status values are mutually exclusive, so a control cannot be
* both valid AND invalid or invalid AND disabled.
*/
readonly status: string;
`angular-typesafe-reactive-forms-helper:
`typescript
export type ControlStatus = 'VALID' | 'INVALID' | 'PENDING' | 'DISABLED'; export interface FormGroupTypeSafe extends FormGroup {
readonly status: ControlStatus;
readonly statusChanges: Observable;
}
` Code samples:
`typescript
let sut: FormGroupTypeSafe = createGroup(); // $ExpectType ControlStatus
sut.status;
sut.statusChanges.subscribe(val => {
// $ExpectType ControlStatus
val;
});
// $ExpectType string | undefined
sut.getSafe(x => x.heroName)?.status; // unfortunately this is still string ¯\_(ツ)_/¯
sut.getSafe(x => x.heroName)?.statusChanges.subscribe(val => {
// $ExpectType ControlStatus
val;
});
`---
#### V1.5.1 (2020-04-17)
Had this error in
Angular 9.1.2 when executing ng serve.
The app would show a blank page with an error in browser's devtools console:
`
main.ts:15 Error: Angular JIT compilation failed: '@angular/compiler' not loaded!
- JIT compilation is discouraged for production use-cases! Consider AOT mode instead.
- Did you bootstrap using '@angular/platform-browser-dynamic' or '@angular/platform-server'?
- Alternatively provide the compiler with 'import "@angular/compiler";' before bootstrapping.
at getCompilerFacade (core.js:643)
at Function.get (core.js:16349)
at getFactoryDef (core.js:2200)
at providerToFactory (core.js:17183)
at providerToRecord (core.js:17165)
at R3Injector.processProvider (core.js:16981)
at core.js:16960
at core.js:1400
at Array.forEach ()
at deepForEach (core.js:1400)
`This is fixed.
More info on the error from StackOverflow.
---
#### V1.5.0 (2020-04-15)
Extend
AbstractControlTypeSafe with:`typescript`
readonly valueChanges: Observable
get(path: Array
get(path: number[]): AbstractControlTypeSafe
- Samples readonly valueChanges: Observable:`typescript
let sut: FormGroupTypeSafe
sut.valueChanges.subscribe(val => {
// $ExpectType HeroFormModel
val;
});
sut.getSafe(x => x.heroName).valueChanges.subscribe(val => {
// $ExpectType string
val;
});
`
- Split Angular's get into two functions based on the path: Array parameter.
Angular's forms.d.ts:`typescript`
get(path: Array
angular-typesafe-reactive-forms-helper:
`typescript `
get(path: Array
get(path: number[]): AbstractControlTypeSafe
This allows type safety when working with arrays.
`typescript
sut.getSafe(x => x.weapons).get([0]).valueChanges.subscribe(val => {
// $ExpectType WeaponModel
val;
});
// the angular way - .get('person.name')
sut.getSafe(x => x.weapons).get('person.name').valueChanges.subscribe(val => {
// $ExpectType any
val;
});
// the angular way - .get(['person', 'name'])
sut.getSafe(x => x.weapons).get(['person', 'name']).valueChanges.subscribe(val => {
// $ExpectType any
val;
});
`AbstractControlTypeSafe
---
#### V1.4.0 (2020-04-14)
- new interface
which extends from Angular's AbstractControl and will, over time, contain the common properties to Angular's FormGroup, FormControl and FormArray.readonly value: T
Currently it only returns .
- enhanced getSafe to return AbstractControlTypeSafe
(propertyFunction: (typeVal: T) => P): AbstractControlTypeSafe | null;`typescript`
getSafe`
Code example:typescript`
// heroName: string
sut.getSafe(x => x.heroName)?.value; // value's ExpectType => string | undefined
- add new type RecursivePartialpatchValue
- enhanced to use RecursivePartial so one is not forced by the compiler to complete mandatory properties on a nested types.`typescript`
patchValue(value: RecursivePartial`
Code Example:typescript`
let sut: FormGroupTypeSafe
// Looking at the line below...
// Before V1.4.0, Typescript would have complained about missing property damagePoints.
// This is not the case anymore as now all nested types will be Partial properties.
sut.patchValue({ weapons: [{ name: "Head" }]});
---
#### V1.3.0 (2020-04-06)
- patchValue
Angular's forms.d.ts:`typescript`
patchValue(value: any, options?: Object): void;`
angular-typesafe-reactive-forms-helper:typescript`
patchValue(value: Partial
- formBuilderTypeSafe.group\
`typescript`
sut = formBuilderTypeSafe.group
heroName: new FormControl('He-Man', Validators.required),
weapons: new FormArray([formBuilderTypeSafe.group
name: new FormControl('Sword', Validators.required),
damagePoints: new FormControl(50, Validators.required)
}),
formBuilderTypeSafe.group
name: new FormControl('Shield', Validators.required),
damagePoints: new FormControl(0, Validators.required)
}),
])
});
---
#### V1.2.0 (2020-04-02)
- valueChanges, function returns Observable\
Angular's forms.d.ts:`typescript`
valueChanges: Observable`
angular-typesafe-reactive-forms-helper:typescript`
valueChanges: Observable
---
#### V1.1.0 (2020-03-31)
- setValue, just a function signature update.
Angular's forms.d.ts function signature:`typescript`
setValue(value: {
[key: string]: any;
}, options?: {
onlySelf?: boolean;
emitEvent?: boolean;
}): void;
angular-typesafe-reactive-forms-helper signature:
`typescript``
setValue(value: T,
options?: {
onlySelf?: boolean;
emitEvent?: boolean
}): void;
---
#### V1.0.0 (2020-03-29)
angular-typesafe-reactive-forms-helper has these extra functions:
- getSafe
- setControlSafe