As explained previously, each form element in angular is represented by an instance of FormControl
class which resides in ‘@angular/forms’ package.
In template driven forms, angular creates instances of FormControl
for each form element on its own while in Reactive form type, form elements(or FormControl
instances) are created programmatically using classes provided by angular.
Also, in reactive forms, the entire form is represented as a FormGroup
.
Thus, all the form elements belong to a FormGroup
.
A form can contain other form groups as well which is required when creating complex forms or forms spanning across multiple pages/tabs.
Sample Form
This tutorial will make use of a simple login form with 2 text boxes for user name and password, a check box for remember me functionality and a submit button.
HTML code for the form will be as shown below.
<html> <body> <form> <input type="text" name="userName"/> <input type="password" name="password"/> <input type="checkbox" name="remember"/> <button type="submit">Submit</button> </form> </body> </html>
This is a simple form which does not have any angular specific functionality.
Reactive Form Example
Similar to template based form, while creating a reactive form, certain directives are applied to the form and its elements in HTML template.
But in reactive forms, an additional task is required in component class as well.
This task is creating FormGroup
object for the form(since a form is a group of controls, hence it is considered as a form group) and FormControl
objects for each of the form controls.
Following changes need to done in HTML template and the component class in order to create a reactive form.
A. In Component class
1. An instance of FormGroup
needs to be created.
This corresponds to the entire form in HTML template.
2. Instances of FormControl
need to be created.
Each FormControl
instance corresponds to a form element inside the HTML template.
A FormControl
instance is created using the constructor of FormControl
class.
This constructor takes a value which represents the state of the form control or the initial value of the form element.
Each FormControl
instance is given a name which is used for binding it with the corresponding HTML form element.
B. In HTML template
1. HTML form is associated with the FormGroup
instance created in the component class. This is done by using [formGroup] directive in the <form> tag and binding it with the FormGroup
instance of the component class.
2. Each form element is associated to a FormControl
instance. This is done by using formControlName directive in the HTML form element and binding it with the FormControl
instance created the component class. Binding is done by using the name given to the FormControl
instance in the component class.
Applying the above changes to the sample form, its HTML template will be modified as below.
<html> <body> <form [formGroup] = "loginFormGroup"> <input type="text" formControlName = "username"/> <input type="password" formControlName = "password"/> <input type="checkbox" formControlName = "remember"/> <button type="submit">Submit</button> </form> </body> </html>
Notice the use of formGroup and formControlName directives at the form tag and form elements.
Component class for the above HTML template will be
import { Component } from '@angular/core'; import { FormGroup } from '@angular/forms'; import { OnInit } from '@angular/core/src/metadata/lifecycle_hooks'; import { FormControl } from '@angular/forms'; @Component({ selector: 'login', templateUrl: './login.component.html', styleUrls: ['./login.component.css'] }) export class LoginComponent implements OnInit { // form group declaration loginFormGroup: FormGroup; ngOnInit() { // create form group instance this.loginFormGroup = new FormGroup({ 'username': new FormControl(''), 'password': new FormControl(''), 'remember': new FormControl(true) }); } }
Component creates a FormGroup
instance inside its ngOnInit
method using its constructor.
Constructor of FormGroup
takes an object which is a collection of key-value pairs.
Value of this object is the FormControl
instance and Key of this object is the name with which this FormControl
instance should be identified.
Number of FormControl
objects in this collection should be equal to the number of form elements in the HTML template.
Note that the name of form group variable(loginFromGroup) matches with the value of [formGroup] directive in the <form> tag of HTML template.
Name of each FormControl
instance matches with the name given for that control in the HTML template of this form.
Refer image below to grab a better understanding of the concept.
If the names of formControlName directive in HTML template and FormControl
instance in the component class do not match or there is no FormControl
instance in the component class corresponding to every form element with formControlName directive in the HTML template, then you will get the below error at the browser console.
ERROR Error: Cannot find control with name: ‘username’
As mentioned earlier, constructor of FormControl
takes a value which represents the initial state of the form element.
Thus, if the FormControl
instance belongs to a form element which is a text box, then the value supplied to its constructor should be a string and this value will be displayed in the text box.
Similarly, if the FormControl
instance belongs to a form element which is a check box, then the value supplied to its constructor should be either true
or false
.
If it is true
then the checkbox will be displayed as checked or it will be unchecked.
Submitting Reactive Form
In order to submit a reactive form, associate its ngSubmit
event with a method defined in the corresponding component class. ngSubmit
is the output event emitted by the FormGroup
. Thus form tag will be modified as below.
<form [formGroup] = “loginFormGroup” (ngSubmit) = “submitForm()”>
Component class will be modified to contain the submit event handler method.
import { Component } from '@angular/core'; import { FormGroup } from '@angular/forms'; import { OnInit } from '@angular/core/src/metadata/lifecycle_hooks'; import { FormControl } from '@angular/forms'; @Component({ selector: 'login', templateUrl: './login.component.html', styleUrls: ['./login.component.css'] }) export class LoginComponent implements OnInit { // form group declaration loginFormGroup: FormGroup; ngOnInit() { // create form group instance this.loginFormGroup = new FormGroup({ 'username': new FormControl(''), 'password': new FormControl(''), 'remember': new FormControl(true) }); } /** * Submit event handler method */ submitForm() { console.log(this.loginFormGroup.value); } }
Above event handler method is called when the form is submitted and it prints the form value. It prints the following output.
Keys are the names with which form controls were created(username, password and remember) while values are the values typed by the user.
{username: “DemoUser”, password: “1234”, remember: true}
password: “DemoUser“
remember: true
username: “1234“
In order to submit the form to the server, you could collect all the form control values into an object and send it to server using http service provided by angular.
Thus submitForm()
method can be modified as below.
submitForm() { // initialize object to hold form data const serverData = {}; // get form values const formData = this.loginFormGroup.value; // get value of user name and set it in object formData['username'] = formData['username'] formData['password'] = formData['password'] formData['remember'] = formData['remember'] // code to send form data to server using http service }
Conclusion
Reactive forms offer a powerful and flexible way to handle form input validation and management in Angular applications.
Through this article, we’ve explored the fundamental concepts of reactive forms, understanding their dynamic nature and ability to react to changes in user input.
The Reactive Form Example section provided a hands-on demonstration of creating a reactive form, defining form controls, applying validation rules, and accessing form data.
Finally, we discussed the process of submitting a reactive form, emphasizing the importance of form validity checks before processing form data.
By mastering reactive forms, developers can streamline the development process, enhance user experience, and ensure data integrity in their Angular applications.