Browse Articles

Angular Model-Driven (Reactive) Forms

 Print   8 min read  
22 Jul 2022
Intermediate
20.8K Views

Forms come in handy when handling user input and enabling users to log in, update information, and do other data-entry tasks. These are used to capture user input events. In Angular, there are two approaches to handling user inputs.

  • Template-Driven Forms

  • Reactive Forms

Template Driven Forms

Template Driven Forms are used to bind the data to the component class using ngModel. We do not need to take the pain of writing code and most of the task is done by Angular itself. There is very less of effort required from the developer to pass information from the component class to the template.

Reactive forms

However, Reactive forms are synchronous and are usually considered when working with databases. We write the code for validation inside the component class and also map the objects to the form controls on the view.

Using Reactive forms, we will be able to create and manipulate various form-control objects directly into the component itself. because the component class has all the access to the form control structure and the data model, so we can push model values into the form controls and also be able to check for the updated values that have been changed by the end-user. The component can observe constantly against changes in the form-control state and react to them accordingly.

In this article, our focus is to work with Reactive Forms which follow the model-driven approach to work with user input and handle it dynamically.

GETTING STARTED WITH REACTIVE FORMS

We will create a Form, using FormGroup, FormControl, FormBuilder class, etc. The very first step is to import ReactiveFormsModule in the app.module.ts as listed below :

 
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import {ReactiveFormsModule} from '@angular/forms';
import { AppComponent } from './app.component';

@NgModule({
 declarations: [
 AppComponent
 ],
 imports: [
 BrowserModule, ReactiveFormsModule
 ],
 providers: [],
 bootstrap: [AppComponent]
})
export class AppModule { }

Now there are some important classes that need to be imported inside the component like FormControl, FormArray like listed below :

 
import { Component } from '@angular/core';
import {FormArray, FormControl, FormGroup} from '@angular/forms';

@Component({
 selector: 'app-root',
 template: ``
})
export class AppComponent {
 title = 'Reactive Forms';

}

Working with FORMCONTROL Class

The FormControl class takes three parameters as input :

  • Initial data value

  • Validators (synchronous)

  • Validators (Asynchronous)

We will focus on validations in the latter portion of this article.

 
export class AppComponent {
 title = 'Forms';
 email= new FormControl('');
}

On the template, we can use this FormControl using :

 
<input [FormControl] = "email" type ="text" placeholder="What is your email?">

Working with FormGroup Class

Form Controls comprise to become FormGroup. We can think of FormControl as a child property of FormGroup. The syntax used for FormGroup is as follows

 
export class AppComponent {
 title = 'Forms';
 SignIn = new FormGroup({
 email: new FormControl(''),
 pwd: new FormControl('') 
})
}
To use it inside the template
 
 <form [formGroup]= "SignIn" novalidate class= "form">
 <input [formControl] = "email" type ="text" class="form-control" placeholder="What is your email?"/>
 <input [formControl] = "pwd" type ="text" class="form-control" placeholder="What is your password?"/>
 </form>

Working with FormBuilder Class

To simplify the syntax of FormGroup and FormControl, we use FormBuilder. This helps reduce the length of our code and optimize it.

 
constructor(private ff : FormBuilder){
this.SignIn= this.ff.group({
email: [null, [Validators.required, Validators.minLength(4)]],
pwd: [null, [Validators.required, Validators.maxLength(8)]]
})
}

Working with FormArray Class

If you want to add fields in the form dynamically with the help of an array, FormArray is used. Like other classes, it is imported in the component like this :

 
import {FormArray} from '@angular/forms';
To use it inside the component
 
this.SignIn = this.ff.group({
 email: '',
 pwd: ''
 items: this.ff.array([ this.crItem() ])
 });
On the view
 
 <div formArrayName="items"
 *ngFor="let item of SignIn.get('items').controls; let count = index;">
 <div [formGroupName]="count">
 <input formControlName="email" ">
 <input formControlName="password" >
 </div>
 List: {{ SignIn.controls.items.controls[count].controls.name.value }}
 </div>

Validations

Till now, we did not add any validation checks to our elements, however, we can do that using Validators in Angular Forms.

 
import { FormGroup, FormControl, Validators } from '@angular/forms';

Let us try adding validation to the email field that we created. Inside the component class

 
ngOnInit() {
 this.loginForm = new FormGroup({
 email: new FormControl(null, [Validators.required]),
 password: new FormControl(null, [Validators.required, Validators.maxLength(8)]),
 class: new FormControl(null)
 });
}

On the template

 
<input formControlName="email" type="text" class="form-control" placeholder="Enter Email" />
<div class="alert alert-danger" *ngIf="loginForm.get('email').hasError('required') && loginForm.get('email').touched">
 Email is required
</div>
 <input formControlName="password" type="password" class="form-control" placeholder="Enter Password" />
 <div class="alert alert-danger" *ngIf="!loginForm.get('password').valid && loginForm.get('email').touched">
 Password is required and should less than or equal to 8 characters
 </div>

This will give output as :

Creating a Reactive Form

Step 1: Import ReactiveFormsModule in App Module

Step 2: Import FormControl, FormGroup classes in the component class

Step 3: Create a FormGroup class with details like email, password

Step 4: On the template, create a Submit button with the function to implement submit in the class.

Putting together everything

 
import { Component, OnInit } from '@angular/core';
import { FormGroup, FormControl, FormArray, Validators } from '@angular/forms';
@Component({
 selector: 'app-root',
 template:`
 <div class="container">
 <br />
 <form (ngSubmit)='loginUser()' [formGroup]='loginForm' novalidate class="form">
 <input formControlName='email' type="text" class="form-control" placeholder="Enter Email" />
 <div class="alert alert-danger" *ngIf="loginForm.get('email').hasError('required') && loginForm.get('email').touched">
 Email is required
 </div>
 <input formControlName='password' type="password" class="form-control" placeholder="Enter Password" />
 <div class="alert alert-danger" *ngIf=" !loginForm.get('password').valid && loginForm.get('email').touched">
 Password is required and should less than or equal to 8 characters
 </div>
 <button [disabled]='loginForm.invalid' class="btn btn-default">Login</button>
 </form>
</div> 
 `
})
export class AppComponent implements OnInit { 
 loginForm: FormGroup; 
 ngOnInit() { 
 this.loginForm = new FormGroup({
 email: new FormControl(null, [Validators.required, Validators.minLength(4)]),
 password: new FormControl(null, [Validators.required, Validators.maxLength(8)])
 });
 }
 loginUser() {
 console.log(this.loginForm.status);
 console.log(this.loginForm.value);
 }
}
On the browser

Creating Custom Validations for Angular Reactive Forms

Now that we have applied validation to email and password, for class we want to create a validation for range such that the class should be from 1 to 12 only. In this case, we will have to write a custom validator as Angular doesn’t provide range validation. So for that, we need to create a function that takes one input parameter of type AbstractControl and returns an object of key-value pair in case the validation fails.

 
function classValidator(control: AbstractControl) : {[key : string] : boolean} | null {
 return null;
 }

Here we have created a custom validator named classValidator where the user will be able to enter a class only if it’s in the range that we have provided it. In case of failure of validation, it will return an object which contains key-value pair.

In the component class
 
class: new FormControl(null, [classValidator])

On the Template, it goes like this:

 
<input formControlName='Class' type="number" class="form-control" placeholder="Enter Class" />
 <div class="alert alert-danger" *ngIf="loginForm.get('class').dirty && loginForm.get('class').errors && loginForm.get('class’).errors.classRange ">
 Class should be in between 1 to 12 
 </div>
display

This is how Custom validators help us put validation to our form controls.

SUMMARY

Reactive forms are just the opposite of what does in template-driven forms it means that instead of defining the form and its controls itself into the template, you can define the form structure into the component file which is a pre-defined structure based on the form structure.

Even, we can say that the Reactive forms are model-driven approaches in contrast to the template-driven approaches. in that case, we do have full control over a form element, form controls, and their respective validation configuration.

In this article, we discussed what are Reactive Forms in Angular. We also saw some of the building blocks of reactive forms and that is why reactive forms are beneficial to use. We also created a basic reactive form with some built-in validations in it. Then we created our custom class validator and applied it to the form.

Learn to Crack Your Technical Interview

Accept cookies & close this