In today’s world, most of the application contains the forms. It can be a simple form or can be a complex one.
So forms are a crucial part of any application that we are building.
In this post, we’ll learn how to create Reactive Forms in Angular.
Angular provides two different ways to works with the form:
- Template Driven Forms
- Reactive Forms(Model Driven Forms)
Template drive form is the same way that we were using in Angular1(AngularJs). It binds the data using data model property in the component, using ngModel
directive. In this approach angular updates the mutable data model with user changes. Reactive Forms uses totally a different way to create forms. It uses an immutable approach to manage the state of the form. so when the user changes anything in the form will result in a new form state.
We’ll convert this article into Angular Reactive Form Series. so that it will be clearer to get a better understanding of angular reactive forms.
This series contains a list of articles as follows:
1. Angular Reactive Forms: Getting Started.
2. Angular Reactive Forms: Dynamic Control with Form Array [Coming soon].
3. Angular Reactive Forms: Built-in Validators.
4. Angular Reactive Forms: Custom Validator.
Table of Contents
Table Of Contents:
1) Importing ReactiveFormsModule.
2) Building Forms Using Reactive Forms.
– FormControl
– FormGroup
– FormBuilder
– FormArray
3) Managing Control Values.
4) Saving Form Data.
5) Managing Form Values.
6) Creating FormControl with FormBuilder.
1. Importing ReactiveFormsModule
To get started with Reactive Forms, we need to import ReactiveFormsModule
from @angular/forms
in Main module/Root Module which is app.module.ts
.
app.module.ts
will look as follows:
import { NgModule } from '@angular/core';
import { ReactiveFormsModule } from '@angular/forms';
@NgModule({
imports: [
// other imports ...
ReactiveFormsModule
]
})
export class AppModule { }
Note: If you are using Template Driven Form then you need to import FormsModule in above code instead of ReactiveFormsModule.
2.Building Forms Using Reactive Forms
To get started with Reactive Forms, we need to know about the following classes:
1) FormControl: It is a basic building block when using ReactiveForm. FormControl is an individual form input, which tracks the value and validation status of an individual form control. Input can be a textbox, textarea, checkbox, radio, select etc.
2) FormGroup: A group of form controls, which track the value and validity state of group of FormControl instance.
3) FormArray: It Tracks the value and validity state of an array of FormControl, FormGroup or FormArray instances
4) FormBuilder: It simplifies the form. so instead of using FormControl, FormGroup and FormArray directly, we can use a service API called FormBuilder that simplifies the Form.
FormControl
Basic Example
import { Component } from "@angular/core";
import { FormControl, FormGroup } from "@angular/forms";
@Component({
selector: "my-app",
templateUrl: "./app.component.html",
styleUrls: ["./app.component.css"]
})
export class AppComponent {
profileGroup:FormGroup
ngOnInit() {
this.firstName = new FormControl('');
}
}
Declaring FormControl
is easy as you can see in the above code. You need to import FormControl
from @angular/forms
packages.
To bind firstName
property in template html, we can do the following:
<form novalidate="">
FirstName: <input type="text" [formcontrol]="firstName">
</form>
If we have single form control, then we can create it as above. but if we need multiple form controls then we can wrap it in the group, that’s where FormGroup
comes into the play.
FormGroup
Basic Example:
import { Component } from "@angular/core";
import { FormControl, FormGroup } from "@angular/forms";
@Component({
selector: "my-app",
templateUrl: "./app.component.html",
styleUrls: ["./app.component.css"]
})
export class AppComponent {
profileGroup:FormGroup
ngOnInit() {
this.profileGroup = new FormGroup({
firstName: new FormControl("Elite"),
lastName: new FormControl("Corner")
});
}
}
Now we’ve profileGroup
which is using FormGroup
instance and FormGroup
accepts object which contains a group of form control. so in the above example, we have two FormControl
instance called firstName
and lastName
. That’s how we can bind a list of form controls in formgroup.
Binding this FormGroup
and FormControl
instance is easy task.
We can bind it in our template html file as following:
<form [formgroup]="profileGroup" novalidate="">
FirstName: <input type="text" formcontrolname="firstName">
LastName: <input type="text" formcontrolname="lastName">
</form>
As you can see in the above code that we’ve used [formGroup]
to bind the profileGroup
and we have also declared child FormControl
instance as formControlName
and assign fieldName
Now, We’ll create one component called ProfileComponent
and we’ll use profileForm
in ProfileComponent
.
To create component run the following command:
ng generate component profile
ProfileForm contains the following Fields:
- FirstName
- LastName
Our profile.component.ts
will looks as follows:
import { Component, OnInit, ChangeDetectionStrategy } from "@angular/core";
import { FormGroup, Validators, FormControl } from "@angular/forms";
@Component({
selector: "my-profile",
templateUrl: "./profile.component.html",
styleUrls: ["./profile.component.css"]
})
export class ProfileComponent implements OnInit {
profileForm: FormGroup;
submitted: boolean = false;
constructor() {}
ngOnInit() {
this.profileForm = new FormGroup({
firstName: new FormControl("", Validators.required),
lastName: new FormControl("", Validators.required),
email: new FormControl("", Validators.email)
});
}
onSubmit() {
this.submitted = true;
}
}
As you can see in above code we’ve initialized profileForm
which is FormGroup
and it contains three form controls which are firstName
, lastName
and email
.
And also you can see that we’ve added required
and email
validators as well. If you are new to angular validators, I would recommend reading my previous article: Reactive Forms With Built In Validators Example
Once we create control in component class, we need to associate it with form control element in template. so now we’ll see how we can bind profileForm
to it.
As I said earlier we can use [formGroup]
to bind profileForm
and we can use formControlName
for each field as follows :
<form novalidate="" [formgroup]="profileForm">
First Name : <input type="text" placeholder="First Name" formcontrolname="firstName">
Last Name : <input type="text" placeholder="Last Name" formcontrolname="lastName">
Email : <input type="text" placeholder="Email" formcontrolname="email">
<button type="submit">Submit</button>
</form>
The form control is now registered to firstName, lastName and email input element in the template using template binding syntax. It has generated two-way data binding between model and template.
The form control and DOM element communicate with each other: the view reflects changes in the model, and the model reflects changes in the view.
3.Managing Control Values
Reactive Form allows managing the state and value of form control instance. We can get the value of control in the following 2 ways: 1) Inside Component class Using valueChanges
observable, which will listen for the value change of form control’s value.
this.profileForm.get('firstName').valueChanges.subscribe((newVal) => {
console.log(newVal);
})
For the performance, you can use the above code as following using debounceTime
. basically what it does is it will wait for specified time and then it will emit the value.Form Control Change
import { debounceTime } from "rxjs/operators";
//....
//....
this.profileForm.get('firstName').valueChanges.pipe(debounceTime(500)) .subscribe((newVal) => {
console.log(newVal);
})
Above code will wait for 500ms/0.5s after control value change and then it will emit the value.
The same way we can detect the changes of form. so if any of the fields in the form will be changed then it will emit the value. we just need to remove get('firstName')
from the above code.
Form Change
import { debounceTime } from "rxjs/operators";
//....
//....
this.profileForm.valueChanges.pipe(debounceTime(500)).subscribe((newVal) => {
console.log(newVal);
})
Updating Form Control Value
To update the value of form control we can use setValue
function of FormControl
.
If we want to change the value of firstName
to Elite Corner, we can use it as follows:
this.profileForm.get('firstName').setValue('Elite Corner');
4.Saving Form Data
The FormGroup
directive listens for the submit event emitted by the form element and emits an ngSubmit
event that you can bind to a callback function.
<form [formGroup]="profileForm" (ngSubmit)="onSubmit()">
The above code will call the onSubmit
function when form is submitted or submit button is clicked. In onSubmit
function we can check that if submitted form is valid or not and based on that we can do further actions.
onSubmit() {
if (this.profileForm.valid) {
//form is valid
//you can pass it to api endpoint
} else {
//show error
}
}
That’s how we can check that form is valid or not using this.profileForm.valid
Sometimes we have a situation where we need the value of each field in the form. we can check it as follows:
this.profileForm.value
5.Managing Form Values
While updating the form value we often have two scenarios:
1) Updating part of the model/form (only some fields)
2) Updating the whole model/form (all fields)
Updating part of the model/form
Use patchValue while you are updating part of the model.
Don’t use setValue
while updating part of the model, otherwise it will throw an error.
Updating the whole model/form
Use setValue
when you are updating all the fields of model
If we continue our profileForm
example, and if we want to update part of the model, we can do as follows:
this.profileForm.patchValue({
firstName: 'abc'
})
and to update all the fields:
this.profileForm.setValue({
firstName: 'abc',
lastName:'xyz',
email:'abc@xyz.com'
})
6.Creating Form Control with Form Builder
Creating form control instances manually can become repetitive when dealing with multiple forms.
The FormBuilder
service provides convenient methods for generating controls to simplify the process of creating form control.
We can create the same profileForm
using FormBuilder
as following:
Import FormBuilder
import { FormBuilder } from '@angular/forms';
Inject FormBuilder Service
constructor(private fb: FormBuilder) { }
Creating Form
this.profileForm = this.fb.group({
firstName: ["", Validators.required],
lastName: ["", Validators.required],
email: ["", Validators.email]
});
In the example above, we’ve used the group()
method with the same object as profileName
to define the properties in the model. The value for each control name is an array containing the initial value as the first item in the array.
I hope you like this post. Please share it with your friends if you like it.
Also Read:
- Angular Reactive Forms With Built In Validators
- Template Reference Variables(#var) in Angular
- How to get names and values of enum in typescript
- Different ways to read Local JSON file in Angular with example
- Angular Material Date Range Picker
- Angular Material Grid Layout Example
- Top 10 VS Code extensions
- Angular Email Validation in Template-Driven Form with Example