ng-typesafe-formgroup
Version:
A ligthweight (just 19kb) Angular Reactive Form package that makes it's controls type safe and values asserted.
250 lines (192 loc) • 8 kB
Markdown
# Angular TypeSafe Reactive Forms
Reactive FormGroups Wrapped in a Type Safe Class
- Improvements suggesions, bugs, errors, pull requests or become a contributor? Everything you need: https://github.com/pheetah/Angular-Type-Safe-Reactive-FormGroup
- Check this examples on stackblitz: https://stackblitz.com/edit/ng-typesafe-formgroup?file=src%2Fapp%2Fapp.component.html,src%2Fapp%2Fapp.component.ts
## Installation and Basic Usage Guide
*Implemented on Angular v13.2.x*
<br />
Please click on dropdowns below for further information:
<details><summary style="font-weight:bold;">Installation and Basic Integration</summary>
<p>
<ul style="list-style-type: upper-roman;">
<li>
To install the package just write the command:
```
npm i ng-typesafe-formgroup
```
</li>
<li>
Make sure that you import <i>FormsModule</i> and <i>ReactiveFormsModule</i> to the related module!
```ruby
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
export class ...Module { }
```
</li>
<ul>
</p>
</details>
<details><summary style="font-weight:bold">Basic Usage</summary>
<ul style="list-style-type: upper-roman">
<li> Either use in strong-typed fashion </br>
```ruby
import { FormControlTypeSafe, FormGroupTypeSafe } from 'ng-typesafe-formgroup';
interface CustomInterface{
name: string,
email: string,
message: number,
};
export class AppComponent {
typeSafeFormGroup = new FormGroupTypeSafe<CustomInterface>({
name: new FormControlTypeSafe<CustomInterface["name"]>('', [Validators.required, Validators.minLength(5)]),
email: new FormControlTypeSafe<CustomInterface["email"]>('', [Validators.required, Validators.maxLength(30)]),
message: new FormControlTypeSafe<CustomInterface["message"]>('', [Validators.required, Validators.maxLength(100)])
});
\* you can now directly reach the controls! *\
ngOnInit(){
this.typeSafeFormGroup.controls.name
this.typeSafeFormGroup.controls.message
this.typeSafeFormGroup.controls.email
this.typeSafeFormGroup.controls.email.value
this.typeSafeFormGroup.controls.email.valueChanges.subscribe(a => console.log(a));
this.typeSafeFormGroup.valueChanges.subscribe(a => console.log(a));
}
}
```
</li>
<li> Or use loose typing
```ruby
import { FormControlTypeSafe, FormGroupTypeSafe } from 'ng-typesafe-formgroup';
export class AppComponent {
typeSafeFormGroupTypedWeak = new FormGroupTypeSafe({
x: new FormControlTypeSafe('', [Validators.required])
});
ngOnInit(){
this.typeSafeFormGroupTypedWeak.controls.x.value
this.typeSafeFormGroupTypedWeak.controls.x.valueChanges.subscribe(a => console.log(a));
this.typeSafeFormGroupTypedWeak.valueChanges.subscribe(a => console.log(a));
}
}
```
</li>
</ul>
</details>
<details><summary style="font-weight:bold">Discover the Package</summary>
<h2 style="color: steelblue">Flexibility</h2>
<li> Either use strong typing
```ruby
...
interface CustomInterface{
name: string,
email: string,
message: number,
};
...
typeSafeFormGroup = new FormGroupTypeSafe<CustomInterface>({
name: new FormControlTypeSafe<CustomInterface["name"]>('', [Validators.required, Validators.minLength(5)]),
email: new FormControlTypeSafe<CustomInterface["email"]>('', [Validators.required, Validators.maxLength(30)]),
message: new FormControlTypeSafe<CustomInterface["message"]>('', [Validators.required, Validators.maxLength(100)])
});
...
```
and this way forms will asset your values on <i>typeSafeFormGroup.valueChanges</i> and <i>typeSafeFormGroup.value</i> such as ;
```ruby
typeSafeFormGroup.valueChanges.subscribe(val => val); \* val asserted as CustomInterface *\
typeSafeFormGroup.value \* value asserted as CustomInterface \*
```
so that value is asserted correctly, unlike normal FormGroup class asserts everything as any!
</li>
<br>
<li> Or use loose typing. It still prevents you try to reach undefined control or mistype the control name in the group. But providing interface stricts you on form controls you add.
```ruby
typeSafeFormGroupTypedWeak = new FormGroupTypeSafe({
x: new FormControl('', [Validators.required])
});
constructor(){}
ngOnInit(){
this.typeSafeFormGroupTypedWeak.controls.x /* OK!
this.typeSafeFormGroupTypedWeak.controls.a /* X->error! a is not a member of constructor object
}
```
</li>
<br><br>
</details>
<br />
<br />
<br />
<br />
# Important note
Be careful on value assertions. If you declare and interface and provide it to form group, values asserted on property types of interface. For example:
```ruby
interface CustomInterface{
name: string,
email: string,
message: number,
};
typeSafeFormGroup = new FormGroupTypeSafe<CustomInterface>({
name: new FormControlTypeSafe<CustomInterface["name"]>('', [Validators.required, Validators.minLength(5)]),
email: new FormControlTypeSafe<CustomInterface["email"]>('', [Validators.required, Validators.maxLength(30)]),
message: new FormControlTypeSafe<CustomInterface["message"]>('', [Validators.required, Validators.maxLength(100)])
});
this.typeSafeFormGroup.controls.email.value -> asserted as: string | number , because types on CustomInterfaces are string | number
this.typeSafeFormGroup.controls.email.valueChanges.subscribe(a => console.log(a)); -> 'a' asserted as string | number
this.typeSafeFormGroup.valueChanges.subscribe(a => console.log(a)); -> 'a' asserted as CustomInterface
this.typeSafeFormGroup.value -> asserted as CustomInterface
```
If you don't provide an interface values asserted as "unknown". If you want to assign the value for example to a string (you expect it coming from the form), just basically do:
```ruby
typeSafeFormGroupTypedWeak = new FormGroupTypeSafe({
x: new FormControlTypeSafe('', [Validators.required])
});
let x:string = <unknown> this.typeSafeFormGroupTypedWeak.controls.x.value as string;
this.typeSafeFormGroupTypedWeak.controls.x.valueChanges.subscribe(a => a);
this.typeSafeFormGroupTypedWeak.controls.x.value -> asserted as unknown
```
Please note that you can still use FormControl inside FormGroupTypeSafe instead of FormControlTypeSafe, but it's recommended to use FormControlTypeSafe.
# Type safe forms prevents you from making mistakes
- Transpiler warns you if you try to access an invalid control
```ruby
typeSafeFormGroupTypedWeak = new FormGroupTypeSafe({
x: new FormGroupTypeSafe('', [Validators.required])
});
constructor(){}
ngOnInit(){
this.typeSafeFormGroupTypedWeak.controls.x /* OK!
this.typeSafeFormGroupTypedWeak.controls.a /* X->error! a is not a member of constructor object
}
```
- If you provide an interface, you can not define a control that is not defined in the interface you provide
```ruby
interface FormControls{
name: string,
email: string,
message: number,
};
typeSafeFormGroup = new FormGroupTypeSafe<FormControls>({
name: new FormControlTypeSafe('', [Validators.required, Validators.minLength(5)]),
email: new FormControlTypeSafe('', [Validators.required, Validators.maxLength(30)]),
message: new FormControlTypeSafe('', [Validators.required, Validators.maxLength(100)]),
outlier: new FormControlTypeSafe('', [Validators.required, Validators.maxLength(100)]) // X-> error! 'outlier' is not FormControls property!
})
```