This content originally appeared on Bits and Pieces - Medium and was authored by Dany Paredes
I continue to play with the new features of Angular, and one pending task is to learn about Typed Reactive Forms. The strict forms help us avoid many common issues when working with our forms.
The best way to learn and understand why to use Typed Reactive forms is by showing a scenario. I continue with the project ‘Using Functional Guards In Angular’.
Scenario
In our scenario, we need to add a new field in the purchaseForm, the field amount, and increase it to 0.5, then submit the form.
export class RegisterComponent { purchaseForm = new FormGroup({ name: new FormControl(''), email: new FormControl(''), }); sendForm() { console.log(this.registerForm.value); } }
The Solution
First, add the amount formControl linked in the HTML Markup with input with controlName.
The code looks like this:
Add Taxes
Before sending the form, we need to increase the value because the amount is a string. We must convert it to a number with the Number() function.
Declare the variable priceWithTax to store the result of the operation.
const priceWithTax = Number(this.purchaseForm.controls.amount.value) + 0.5;
Next, using the get method, update the amount field using get and patchValue
this.purchaseForm.get('amount')?.patchValue(priceWithTax)
But I got an error.
src/app/components/register/register.component.ts:17:5 17 amount: new FormGroup('') ~~~~~~~~~~~~~~~~~~~~~~~~~ The expected type comes from property 'amount', which is declared here on type 'Partial<{ name: string | null; email: string | null; amount: string; }>'
The error is because the amount field expects a string value, so parse the to string.
this.purchaseForm.get('amount')?.patchValue(priceWithTax.toString())
It works, but the code is fragile and unclear.
Problems
We use the get method, passing a string to get the amount field it compiles but getting the error in runtime.
The PatchValue method is better because it proposes the available properties in the form.
this.purchaseForm.patchValue({ amount: priceWithTax.toString(), })
Some questions come to my head.
- Why do I need to convert the amount? It is a number :(
- What happens if reset the form, the new value of the amount is null :(
- How do I turn on my form more strictly?
Most of these problems do not exist anymore with Typed Forms.
Convert To Typed Forms
The Reactive Type Forms give us better control and stricter template form checks. It helps complex forms and deeply nested control with type-safety API.
Let’s move to convert my current form to the new reactive strict forms.
FormControls
The FormControl support generic types. We can set the specific type for each field.
purchaseForm = new FormGroup({ name: new FormControl<string>(''), email: new FormControl<string>(''), amount: new FormControl<number>(0) });
So, the amount field is a number, so we no longer need to convert the value to a number.
In compilation, throw an error because it requires a number value.
Error: src/app/components/register/register.component.ts:25:7 - error TS2322: Type 'string' is not assignable to type 'number'. 25 amount: priceWithTax.toString(), ~~~~~~
Remove the toString() and get another error in compilation because the amount should be null.
Error: src/app/components/register/register.component.ts:23:25 - error TS2531: Obj ect is possibly 'null'. 23 const priceWithTax =this.purchaseForm.controls.amount.value + this.PURCHASE _TAX;
Angular 14 provides a new property nonNullable option to tell the number not to be null.
Perfect, we already move the controls to strict types next to FormGroup.
FormGroup
The FormGroup supports generics types so that we can declare all fields required in my forms, like an interface extending from FormGroup.
export interface PurchaseFormModel extends FormGroup<{ name: FormControl<string>; email: FormControl<string>; amount: FormControl<number>; }> { }
Next, assign the interface type for the form.
purchaseForm!: PurchaseFormModel;
Finally, use the formBuilder to create the form for each property.
The form definition must match the interface and doesn’t allow adding extra properties.
constructor(private fb: FormBuilder) { this.purchaseForm = this.fb.group( { name: this.fb.nonNullable.control('hello'), email: this.fb.nonNullable.control('demo@demo.com'), amount: this.fb.nonNullable.control(0), } ) }
The form declaration requires all fields to be not null, so use them this.fb.nonNullable.control if we want to add a nullable field, like cookies. Add in the interface and set type string and null.
export interface PurchaseFormModel extends FormGroup<{ name: FormControl<string>; email: FormControl<string>; amount: FormControl<number>; cookies: FormControl<boolean | null>; }> { }
The field in the form builder uses control with the default value.
constructor(private fb: FormBuilder) { this.purchaseForm = this.fb.group( { name: this.fb.nonNullable.control('hello'), email: this.fb.nonNullable.control('demo@demo.com'), amount: this.fb.nonNullable.control(0), cookies: this.fb.control(true) } ) }
Yeah, We have the form strict and matching with our interface.
One more thing — if we reset the form, it uses the default value in the form controls, not null, as before.
At this point, we have some fully functional form components that we can consider deploying as independent components on a platform such as Bit. That way, we can use and reuse them across new and existing projects whenever we need to.
Conclusion
It was a small slide about Type Reactive Forms in Angular 14/15, check out the code or read more about in the following links:
Learn to build complex forms and form controls quickly
Originally published at https://www.danywalls.com.
Go composable: Build Angular apps faster like Lego
Bit is an open-source tool for building apps in a modular and collaborative way. Go composable to ship faster, more consistently, and easily scale.
Build apps, pages, user experiences and UIs as standalone components. Use them to compose new apps and experiences faster. Bring any framework and tool into your workflow. Share, reuse, and collaborate to build together.
Help your team with:
→ Micro-Frontends
→ Design Systems
→ Code-Sharing and reuse
→ Monorepos
Learn more:
- Getting Started with a New Angular Project in 2023
- How We Build Micro Frontends
- How to Share Angular Components Between Projects and Apps
- How we Build a Component Design System
Using Strictly Typed Reactive Forms in Angular was originally published in Bits and Pieces on Medium, where people are continuing the conversation by highlighting and responding to this story.
This content originally appeared on Bits and Pieces - Medium and was authored by Dany Paredes
Dany Paredes | Sciencx (2023-04-21T09:38:48+00:00) Using Strictly Typed Reactive Forms in Angular. Retrieved from https://www.scien.cx/2023/04/21/using-strictly-typed-reactive-forms-in-angular/
Please log in to upload a file.
There are no updates yet.
Click the Upload button above to add an update.