Over a million developers have joined DZone.
{{announcement.body}}
{{announcement.title}}

How to Create Reactive Forms in Angular

DZone's Guide to

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!

· Web Dev Zone ·
Free Resource

Learn how error monitoring with Sentry closes the gap between the product team and your customers. With Sentry, you can focus on what you do best: building and scaling software that makes your users’ lives better.

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 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 FormGroupFormControl, 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 FormControlclass. The FormControl constructor takes three parameters:

  1. Initial data value, which can be null.
  2. An array of synchronous validators. This is an optional parameter.
  3. 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

FormGroupis a group of FormControls. You can encapsulate various FormControls inside a FormGroup, which offers an API for:

  1. Tracking the validation of a group of controls or form.
  2. 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);
    }
}

What’s the best way to boost the efficiency of your product team and ship with confidence? Check out this ebook to learn how Sentry's real-time error monitoring helps developers stay in their workflow to fix bugs before the user even knows there’s a problem.

Topics:
reactive forms ,angular ,web dev ,web application development

Published at DZone with permission of

Opinions expressed by DZone contributors are their own.

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}