Angular: Build reactive forms using Formbuilder

Coding In depth
May 13 · 5 min read

Forms are a very important part of the web application. Sometimes we come to a scenario where forms element is static and well defined. Sometimes web application development goes beyond static forms to dynamic forms. Dynamic forms do not have static fields. Dynamic forms and controls can be generated based on certain events. In this article, we are going to build a reactive form using an Angular reactive approach.

Photo by chuttersnap on Unsplash

Angular supports two designs for dynamic control.

  • Template Driven- In template-driven forms have more control over the angular template. Two-way binding. Easy to implement.
  • Reactive Forms- Very powerful and most of the control is in the component. Need to Formbuilder to build the form. In this article we are going to use reactive form approach.

Why Reactive Forms?

Reactive forms are a more flexible way to create dynamic forms. After creating both template and reactive forms I found following benefits of creating reactive forms:

  • Very flexible and give all the control in developers hands for creating bindings, error handling, etc. This leads to more code in component than template. There is no binding, so immutable data model.
  • Very easy to write unit test. Since most of the code is in the component so it is very easy to write unit-test.
  • As compare to a template-driven approach, reactive forms are more complex. But once developers get used to it, it is very easy to play with reactive forms.

Building Reactive component

We are building a product reactive component. The product has a product name and product code. The product has another optional parameter that is product features. Product features contain feature name, feature added date, and feature description. The features can be added on the fly. There is no limit to the number of features we are going to add. The screen will look like this.

We are creating a reactive child component product features. We are using two-way data binding between the parent and child components. productField is two-way binding.

<app-product-feature [(productField)]=”productField”></app-product-feature>

Now add reactive form module in-app module file. Reactive form modules will enable to design reactive form the component. Complete app.module.ts will look like the below code.

Create the product child component. Product child component contains template, CSS, and component file. Check the GitHub code in the last attachment. Import FormBuilder, Validators, and FormArray from @angular/forms. FormBuilder is required to design the product form model. Validators is required for input validation of control. FormArray is required for building dynamic form features.

import { FormBuilder,Validators,FormArray } from ‘@angular/forms’;

Define productField as input property. Since we have two-way binding with child component define productFieldChange as output decorator property. Inject FormBuilder as fb in the constructor.

@Input() productField: Product<productFeatures>; 
@Output() productFieldChange = new EventEmitter<any>();
constructor(private fb: FormBuilder) { }

Define the product features product name and code. Product name as required field. Product feature as form Array.

productFeatureForm = this.fb.group({ 
productName: [‘’, Validators.required],
productCode: [‘’],
productFeatures: new FormArray([]) });

For simplicity define two get properties. Get f() and get p().

get f() { return this.productFeatureForm.controls; }get p() { return this.f.productFeatures as FormArray; }

When component initialize add a default feature in forms array. Same object we will add in product feature on the click of add button.

ngOnInit() {this.p.push(this.fb.group({ featureName: [‘’],featureAddedDate: [‘’],featureDescription: [‘’]})); }

The complete component file looks like this.

In the product component feature HTML add the form group property. Define the product name as a required field.

<form *ngIf=”productFeatureForm” [formGroup]=”productFeatureForm”><div class="row required-field"><div class="col-md-4"><label class="cus-form-label">Product Name</label><input class="form-control minimal" id="productName" formControlName="productName" required></div></div></form>

Since product features are inside the product as a form array. Iterate the product features to display multiple features.

<div *ngFor="let prod of p.controls; let i = index" class="list-group list-group-flush"><div class="list-group-item"><h5 class="card-title">Product Feature {{i + 1}}</h5><div [formGroup]="prod" ><div class="row required-field"><div class="col-md-4"><label class="cus-form-label">Feature Name</label><input class="form-control minimal" id="{{'name' + i}}" formControlName="featureName"></div></div></div></div></div>

Complete HTML file looks like below.

Validations

For validation of required mandatory field is isError property. Once submit the form or add new feature make isError to true. Check the below code block for more details.

<div *ngIf=”isError && f.productName.errors” class=”invalid-feedback”><div *ngIf=”f.productName.errors.required”>Product name is required field</div></div>

Add and Delete Product Features

When a new feature is added, add the feature object in the product form object. Once adding done emit the changes to the parent component.

addNewProdField(index: number): void {this.p.push(this.fb.group({featureName: [‘’],featureAddedDate: [‘’],featureDescription: [‘’]}));this.productFieldChange.emit(this.productFeatureForm.value);}

Remove the product based on the index. Once deleting done emit the changes to the parent component.

removeNewProdField(index: number): void {this.p.removeAt(index);this.productFieldChange.emit(this.productFeatureForm.value);}

Whoa, now we can add and delete product features. Adding deleting will notify parent components.

In parent component put below code to check the property change from child component.

<h5>Parent component</h5><pre>{{productField | json}}</pre>

You can see the JSON object from the child component.

{
“productName”: “Netgear”,
“productCode”: “NG001”,
“productFeatures”: [
{
“featureName”: “Wireless”,
“featureAddedDate”: “05/12/2020”,
“featureDescription”: “to connect wireless”
}
]
}

Summary

We have created a reactive form in this article using the form builder. We have also gone through a dynamic product feature component. Feature component can be dynamically added, deleted. Adding the GitHub link that offers download and more flexibility to learn.

A note from Plain English

Did you know that we have launched a YouTube channel? Every video we make will aim to teach you something new. Check us out by clicking here, and be sure to subscribe to the channel 😎

JavaScript In Plain English

New articles every day.

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch
Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore
Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store