How to Create Your First Angular Reactive Form

Dhananjay Kumar / Monday, December 11, 2017

Out of the box, Angular provides us with two types of forms:

  1. Template Driven Forms
  2. 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 ReactiveFormsModle in the App Module as shown in the next listing:

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, FormArray in the components class. We will use these classes to construct our reactive form.  After importing these classes, the component class should look like the listing 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:

  1. Initial data value, which can be null.
  2. Array of synchronous validators. This is an optional parameter.
  3. Array of asynchronous validators. This is an optional parameter.

In the component class, you can create a FormControl as shown in the listing 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:

  1. Tracking the validation of group of controls or form
  2. Tracking the value of 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 next listing:

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 a FormGroup on the view as shown in next listing:

<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 your FormGroup with the form and 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 with elements. You can find value and status of the form by using value and status property. Now, you no longer need to use template reference variable to find status and value of the form.

Step 5:  Submitting 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 listing below:

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 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 FormControls. 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.

<div class="alert  alert-danger"
     *ngIf="loginForm.get('email').hasError('required') && loginForm.get('email').touched">
    Email is required
</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 in the listing 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 in the listing 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 in the listing 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);
    }
}

Like this post?

And there you have it! If you like this post, please share it. In addition, if you haven’t checked out Infragistics Ignite UI for Angular Components, be sure to do so! They’ve got 30+ material based Angular components to help you code speedy web apps faster.