How to Create Reactive Forms in Angular
In this post, we got over a how to create reactive forms in your Angular application step-by-step. Read on to get started coding!
Join the DZone community and get the full member experience.
Join For FreeOut of the box, Angular provides us with two types of forms:
- Template Driven Forms
- Reactive Forms
In Template Driven Forms, you create controls on the component template and bind data using ngModel. With these, you do not create controls, form objects, or write code to work with pushing and pulling of data between component class and template; Angular does all of these tasks for you. In template driven forms, there is very little code for validations in the component class, and they're asynchronous.
In reactive forms, you create form controls as trees of objects in the component class and bind them to the native form controls in the template. All validations and the creation of form controls are written in the component class. In Reactive Forms, all validations and changes in the state of native forms are synchronous, so you can write code in the component class to observe them. You should choose to create reactive forms when the data model is immutable and usually mapped to a database.
The major difference between these two approaches of creating forms? In reactive forms you do not use directives such as ngModel, required, etc. You create all controls and their validations in the component class. Reactive forms are easy to test and maintain, so in this post we will learn to create a basic Reactive Form, using FormControl, FormGroup, FormBuilder class, and adding validations.
Step 1: Add Reactive Forms Module
To start working with reactive forms, first add ReactiveFormsModule
in the App Module as shown in the next code block:
import {ReactiveFormsModule} from '@angular/forms';
import { AppComponent } from './app.component';
@NgModule({
declarations: [
AppComponent
],
imports: [
BrowserModule, ReactiveFormsModule
],
providers: [],
bootstrap: [AppComponent]
})
export class AppModule { }
Step 2: Import Required Module in Component
Next, you need to import the required reactive forms classes such as FormGroup, FormControl, and FormArray in the component class. We will use these classes to construct our reactive form. After importing these classes, the component class should look like what I've got below:
import { Component } from '@angular/core';
import { FormGroup, FormControl, FormArray } from '@angular/forms';
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent {
title = 'Reactive Forms Demo';
}
Your component class can be different from the AppComponent created above depending on your implementation; however, here we have imported FormGroup
, FormControl
, and FormArray
classes. Let's learn about these classes one by one.
Step 3: Using the FormControl class
The FormControl
class corresponds to one individual form control, tracking its value and validity. While creating your reactive form, you will create an object of the FormControl
class. The FormControl constructor takes three parameters:
- Initial data value, which can be null.
- An array of synchronous validators. This is an optional parameter.
- An array of asynchronous validators. This is an optional parameter.
In the component class, you can create a FormControl
as shown in the code below:
export class AppComponent {
email = new FormControl('');
}
We are not passing any optional parameters like sync validations or async validations, but we will explore these parameters while adding validation to a FormControl
.
On the View, you can use email FormControl
as shown below:
<input [formControl]='email'
type="text"
placeholder="Enter Email" />
{{email.value | json}}
As you see, we are using property binding to bind the formControl
email to the input element on the view.
Step 4: Using FormGroup Class
FormGroup
is a group of FormControls. You can encapsulate various FormControls inside a FormGroup
, which offers an API for:
- Tracking the validation of a group of controls or form.
- Tracking the value of a group of controls or form.
It contains child controls as its property and it corresponds to the top lever form on the view. You can think of a FormGroup as a single object, which aggregates the values of child FormControl. Each individual form control is the property of the FormGroup object.
You can create a FormGroup class as shown in the below code:
loginForm = new FormGroup({
email: new FormControl(' '),
password: new FormControl(' ')
})
Here we have created a login form, which is a FormGroup
. It consists of two form controls for email and password. It is very easy to use FormGroup
on the view as shown in the following code:
<form [formGroup]='loginForm' novalidate class="form">
<input formControlName='email'
type="text"
class="form-control"
placeholder="Enter Email" />
<input formControlName='password'
type="password"
class="form-control"
placeholder="Enter Password" />
</form>
{{loginForm.value | json}}
{{loginForm.status | json }}
Here we're using property binding to bind FormGroup
with the form and the formControlName
directive to attach FormControl
to a particular element on the view.
If you have used a template driven form, you will notice that the view is much leaner now: there is no ngModel or name attached to the elements. You can find the value and status of the form by using the value and status property. Now, you no longer need to use a template reference variable to find the status and value of the form.
Step 5: Submitting a Form
To submit the form, let's add a submit button on the form and a function to be called. We'll modify the form as below:
<form (ngSubmit)='loginUser()' [formGroup]='loginForm' novalidate class="form">
<input formControlName='email' type="text" class="form-control" placeholder="Enter Email" />
<input formControlName='password' type="password" class="form-control" placeholder="Enter Password" />
<button class="btn btn-default">Login</button>
</form>
In the component class, you can add a function to submit the form as shown in the below code:
export class AppComponent implements OnInit {
loginForm: FormGroup;
ngOnInit() {
this.loginForm = new FormGroup({
email: new FormControl(null, Validators.required),
password: new FormControl()
});
}
loginUser() {
console.log(this.loginForm.status);
console.log(this.loginForm.value);
}
}
Here we've just added a function called loginUser
to handle the form submit event. Inside this function, you can read the value and status of the FormGroup
object loginForm
using the status and value properties. As you can see, this gives you an object which aggregates the values of individual form controls.
Step 6: Adding Validations
You may have noticed that we haven't added any validation to the forms. Let us start by adding a validation to FormControl
. To do so, import Validators
from @angular/forms:
ngOnInit() {
this.loginForm = new FormGroup({
email: new FormControl(null, Validators.required),
password: new FormControl(null, [Validators.required, Validators.maxLength(8)])
});
}
On the template, you can use the FormGroup
get method to find an error in a particular form control and use it. In the listing below, we are checking the validation error for an email and displaying the error div.
You can also disable your submit button by default, and enable it when the form is valid to allow for submission. This can be done as shown below:
<button [disabled]='loginForm.invalid' class="btn btn-default">Login</button>
Putting everything together, the template with reactive forms will look like the following:
<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 10 characters
</div>
<button [disabled]='loginForm.invalid' class="btn btn-default">Login</button>
</form>
</div>
The component class will be as shown below:
import { Component, OnInit } from '@angular/core';
import { FormGroup, FormControl, FormArray, Validators } from '@angular/forms';
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
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);
}
}
Step 6: Using FormBuilder
FormBuilder
is used to simplify the syntax for FormGroup
and FormControl
. This is very useful when your form gets lengthy. Let's refactor loginForm
to use FormBuilder
. To do so, first import FormBuilder
from @angular/forms
. Then inject it to the component as shown below:
constructor(private fb : FormBuilder){
}
You can use FormBuilder
to create a reactive form as shown in the listing below. As you see, it has simplified the syntax:
this.loginForm = this.fb.group({
email: [null, [Validators.required, Validators.minLength(4)]],
password: [null, [Validators.required, Validators.maxLength(8)]]
})
The template will be the same for both FormBuilder
and FormControl
classes. Putting everything together, the component using FormBuilder
to create a reactive form will look like this:
import { Component, OnInit } from '@angular/core';
import { FormGroup, FormControl, FormArray, Validators, FormBuilder } from '@angular/forms';
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent implements OnInit {
loginForm: FormGroup;
constructor(private fb: FormBuilder) {
}
ngOnInit() {
this.loginForm = this.fb.group({
email: [null, [Validators.required, Validators.minLength(4)]],
password: [null, [Validators.required, Validators.maxLength(8)]]
})
}
loginUser() {
console.log(this.loginForm.status);
console.log(this.loginForm.value);
}
}
Published at DZone with permission of Dhananjay Kumar, DZone MVB. See the original article here.
Opinions expressed by DZone contributors are their own.
Comments