Reactive Form Tutorial in Angular: Converting Complex to Easy

manas_am2
13 min readJun 16, 2020

--

Handling user input with forms is the cornerstone of many common applications. Applications use forms to enable users to log in, to update a profile, to enter sensitive information, and to perform many other data-entry tasks.

Angular provides two different approaches to handling user input through forms: reactive and template-driven. Both capture user input events from the view, validate the user input, create a form model and data model to update, and provide a way to track changes.

Reactive and template-driven forms process and manage form data differently. Each offers different advantages.

In general:

  • Reactive forms are more robust: they’re more scalable, reusable, and testable. If forms are a key part of your application, or you’re already using reactive patterns for building your application, use reactive forms.
  • Template-driven forms are useful for adding a simple form to an app, such as an email list signup form. They’re easy to add to an app, but they don’t scale as well as reactive forms. If you have very basic form requirements and logic that can be managed solely in the template, use template-driven forms.

Common Things Between Them

Both reactive and template-driven forms share underlying building blocks.

  1. FormControl tracks the value and validation status of an individual form control.
  2. FormGroup tracks the same values and status for a collection of form controls.
  3. FormArray tracks the same values and status for an array of form controls.
  4. ControlValueAccessor crates a bridge between Angular FormControl instances and native DOM elements.

Things that we are gonna Create

We are gonna create a usual form that we usually see on every website. Our form will have an input field for first name, last name. The users have to provide email in a proper format and then we will add a password and confirm password input fields too, with a checkbox of Terms & Conditions. Finally, we will have two buttons, one for submitting the form, and the other one is for resetting the values in the form.

Additionally, we are using bootstrap CDN for CSS.

Let’s Code

Whenever we want to do an angular project, then at first we have to create the complete structure of it. And that could be done by a Command Line Interface.

ng new reactive-form

Run the above command in the terminal and your project structure will be ready. Move the whole project to VS-Code to work on it.

Now, when you are all set up to start the journey to this small project then let’s start from the front-end part first. There are two types of CSS in Angular, one is global CSS and another one is local CSS. And under this project, we just have to work with global CSS only.

You need an image for this project too, which you can download from here.

src/style.css is the global CSS file and you have to type the following code under this file to create the background of the web-page.

body {
background: radial-gradient(black 3px, transparent 4px),
radial-gradient(black 3px, transparent 4px),
linear-gradient(#fff 4px, transparent 0),
linear-gradient(
45deg,
transparent 74px,
transparent 75px,
#a4a4a4 75px,
#a4a4a4 76px,
transparent 77px,
transparent 109px
),
linear-gradient(
-45deg,
transparent 75px,
transparent 76px,
#a4a4a4 76px,
#a4a4a4 77px,
transparent 78px,
transparent 109px
),
#fff;
background-size: 109px 109px, 109px 109px, 100% 6px, 109px 109px, 109px 109px;
background-position: 54px 55px, 0px 0px, 0px 0px, 0px 0px, 0px 0px;
}
.container {
margin-top: 10%;
}

As this project is mainly focused on Angular, so I am not able to explain every part of CSS or HTML here. You can copy the above CSS code and paste it in style.css file.

Now, you need to put Bootstrap CDN in your global HTML file too, so that we can use the bootstrap to be fast in the production.

<!-- CSS only -->
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.0/css/bootstrap.min.css" integrity="sha384-9aIt2nRpC12Uk9gS9baDl411NQApFmC26EwAOH8WgZl5MYYxFfc+NcPb1dKGj7Sk" crossorigin="anonymous">

Copy the link and paste it in index.html (src/index.html). Now, we kicked in the bootstrap too, and now its time to run the server.

ng serve

Run the above command to run the server. After running, you will get a localhost link. Paste that link into the chrome browser, and there you can see your progress.

Browser Image after adding Global CSS

Now, we will start the real work with forms.

Open app.module.ts (src/app/app.module.ts). You can consider the module as a parent of a folder and other components as children. Whatever we want to do with children then we have to inform the parents first, otherwise, it is not gonna work. In this module file, we have to import ReactiveFormModule from “@angular/forms”, and we have to declare this inside imports too.

After importing and defining, your module will look like this.

You can see that in the third line, we imported it and under the imports section, we mentioned it again. Now, forms are ready to use in components.

Now, we are gonna work with app.component.ts (src/app/app.component.ts). But, before that, I wanna tell you about OnInit. OnInit is a lifecycle hook that is called after Angular has initialized all data-bound properties. Define an OnInit() method to handle any additional initialization tasks. If you want to read much about ngOnInit, then you can go here.

Now, under this component file, we are gonna import some prerequisite things first, so that it will not trouble us later. So, we are going to import, ngOnInit from “@angular/core”, and FormBuilder, FormGroup, Validators from “@angular/forms”.

After importing OnInit, you can’t use it directly. For that, you have to initialize it by extending the class to implements OnInit, but then you will see an error, which could be resolved by declaring ngOnInit() inside the class.

After doing all the things written above, your component.ts will look like this. But, it’s not complete yet. We still have to go a long way.

Now, after doing the basic things, it’s time to get to know about the default form-validators on Angular.

Just now, you imported the Validators in app.component.ts and we use it to validate the elements in the form, like first name, last name, email, password, etc. So, for creating the elements of the form and validating it too, we have to declare two variables named, registerForm which is equal to FormGroup(we imported it before) and submitted which we put false as a default. We are gonna use registerForm to create the elements of the form but submitted is gonna be used later in the program to check that the user filled every mandatory field or not.

If we provided ngOnInit, then we have to provide a constructor too and inside it, we have to pass an argument, formBuilder which is of the type FormBuilder, that we imported before and this argument is made private so that no one can use it outside this class.

Inside ngOnInit, we bring, registerForm, and made it equal to formBuilder. We assigned .group({}), to this.formBuilder, because group() helps to group all the elements of the form.

Now, inside it, we are providing all the elements that we needed in a form. You can notice, that there are some arguments inside each element, in which the first one is empty and after that, we are validating the element that it is required. Under the email section, we need to validate email too, because we need users to put the correct email. We are using the empty string because we want the user to fill it in the form.

After programming the input element, now, we are moving towards the functioning of buttons. As I mentioned earlier, we are adding two buttons in this form, one is for submission of the form and another one is for the resetting the complete form to default.

In this code, you can notice, that at the bottom we created two functions, named, onSubmit() and onReset(), which will function the submit and reset button, respectfully.

Let’s create the first button in this form, onSubmit(). Whenever we want to submit the form then we want the user to fill all the inputs in the form and if it has done, then we have to change the submitted from false to true. But, we have to check that did user-submitted every element in the form or not and for doing that, we are using if-else. Under if, we are checking that registerForm is invalid (entries not filled properly) and we are not returning anything. Just for testing purposes, we are putting console.table, so that, we can see the changes directly and after that, we are putting an old school thing, “alert”, to get the JSON value of the data that the user is providing to see that what is actually changing when the user provides the input.

Under the second button functioning, we have to reset the form and for that, we are using default reset() function to make it easier.

Now, we have a problem in front of us, that how could we check the password and confirm password is the same. Don’t worry, Angular have the solution for that too. We can use create a custom validator to solve this thing.

There are two ways to create a custom validator. One is by using classes and another is by using functions. And for this project, we are gonna use a functional way to create the custom validator.

For creating a custom Validator, create a new folder in src/app and name it custom-validators and inside that folder, create a typescript file, which is named as password-checker.ts. There is no restriction for naming these files. You can name them anything you want.

Here also, you have to import FormGroup. As I said, we are taking the functional approach to creating this. So, we created a function, named, PasswordChecker and exported it, so that we can import it in the component file to make use of custom validator.

Inside the function, we put two arguments, named and controlName and CompareControlName, and assign them as a string. We are taking these arguments in place of password and confirmPassword. After creating the function, we are putting a callback function for return. Yeah, it’s very weird syntax but that’s what is written in docs of Angular Forms.

We have to pass an argument formGroup of the type FormGroup for the return and then we pass a callback function and inside it, we created two constants named, password and confPassword and we assign them to the argument that we passed for the function. After that, we are checking that does the value of the password and confPassword is the same or not and if it not the same, then we have to use setErrors object, which we have to assign true if error is coming and null if everything is all right.

After creating this custom Validator, we need to import it too inside app.component.ts, so that we can use it there to check the password and confirmPassword.

import {PasswordChecker} from “./custom-validators/password-checker”

PasswordChecker is the name of the function of the custom-validator.

After importing, we have to use this validator inside ngOnInit and we will put password & confirmPassword to check that they are the same or not.

Now, Let’s work on the front-end too. Me and you, both of us want to see our form on web page. But, as I already told you that this tutorial is for angular. So I can’t discuss the basic CSS and HTML part here.

<div class=”container”>
<div class=”card bg-dark text-white m-3 mt-5">
<h5 class=”card-header text-center”>
<img src=”../assets/laptop-coding.png” alt=”” width=”30" height=”30" class=”mr-2" />
Signup Form
</h5>
<div class=”card-body”>
<form >
<div class=”form-row”>
<div class=”form-group col-6">
<label>First Name</label>
<input type=”text” class=”form-control” />
<div class=”text-warning”>
<div>
First Name is required
</div>
</div>
</div>
<div class=”form-group col-6">
<label>Last Name</label>
<input type=”text” class=”form-control” />
<div class=”text-warning”>
<div>
Last Name is required
</div>
</div>
</div>
</div>
<div class=”form-group”>
<label>Email</label>
<input type=”text” class=”form-control” />
<div class=”text-warning”>
<div>Email is required</div>
<div>
Email must be a valid email address
</div>
</div>
</div>
<ng-container>
<div class=”text-danger”>
Value is required.
</div>
</ng-container>
<div class=”form-row”>
<div class=”form-group col”>
<label>Password</label>
<input type=”password” class=”form-control” />
<div class=”text-warning”>
<div>Password is required</div>
<div>
Password must be at least 6 characters
</div>
</div>
</div>
<div class=”form-group col”>
<label>Confirm Password</label>
<input type=”password” class=”form-control” />
<div class=”text-warning”>
<div>
Confirm Password is required
</div>
<div>
Passwords must match
</div>
</div>
</div>
</div>
<div class=”form-group form-check”>
<input type=”checkbox” id=”acceptTerms” class=”form-check-input” />
<label for=”acceptTerms” class=”form-check-label”
>Accept Terms & Conditions</label
>
<div class=”text-warning”>
Accept Ts & Cs is required
</div>
</div>
<div class=”text-center”>
<button class=”btn btn-success btn-lg px-4 mr-3">Register</button>
<button class=”btn btn-warning” type=”reset”>
Reset
</button>
<p class=”text white”>Value: {{ registerForm.value | json }}</p>
<p class=”text white”>Value: {{ registerForm.valid | json }}</p>
</div>
</form>
</div>
</div>
</div>

Copy this HTML code and paste it in your app.component.html. You can notice one thing in this HTML file is that, at last, there are two p tags, where we are using the angular element to print the value of registerForm and validation of the same and we also used pipe in it, to print it in the JSON format. s

But, after running this HTML code on the web, you will notice that only the image is being printed. But we programmed for the whole form. It is happening because this is a component-based on the reactive form and we have to do some of the stuff on it, to make sure that form will be visible.

Open app.component.ts and check which name we gave to our form. Remember, the first variable we created in the component (registerForm). We are doing some property binding stuff in app.components.html

<form [formGroup] =”registerForm”>

Now, save it and open the browser to check your progress.

Yup, it’s looking weird, and you can notice the JSON strings on the bottom of the page. It was there because we did console.table() for testing purposes. With the help of that JSON, we will check that our element is working in real-time or not.

When we created the custom-validator then, in that code, we used controls. Now, we have to use those controls to get every element and we will do this by using get function.

get h(){
return this.registerForm.controls;
}

You have to create this function, in app.component.ts to make it work.

Now, we are gonna add Angular components in app.component.html to make it work nicely with JSON (written at the bottom of the form). To make it work, we have to use two Angular components only, one is formControlName & ngClass. formControlName is going to govern what variable is controlling that input and ngClass is a property binding feature of angular that we are gonna use her to check errors in the element.

As you can see, we used these two elements inside every input field to make sure that it is changing the JSON file.

At first, we are inside the input field of First Name. There we assigned, formControlName= “firstName”, where firstName is the same variable that we used inside the app.component.ts, to assign the value and validator of the First Name. Then we put [ngClass], and inside it, we are putting an inbuilt object of Angular (‘is-invalid’) and we assigned it as submitted and then we are checking that if it is submitted then does it a valid input.

We did the same thing to every input field, till terms and conditions.

When you will save this program and check the progress on the web, then you are able to change the JSON values in real-time.

But, you will notice a thing that every element is changing, instead of email. It is happening because of one bug.

Go to src/app/app.component.ts. Whenever we use more than one Validators in an element, then we have to use one extra bracket.

In this way, change the mistakes that we did earlier and everything will work fine. We even added one more validator to the password also, assigning minLength of 6 to the password.

We are in the final stage of our project. Till now, we worked with ngOnInit, Constructors, Default and Custom Validators, pipes, property binding, and a lot more. And now our form is also visible. But, still, we are able to see the yellowish warning every time on the form and we don’t want it to be there every time. We only want it to kick-in when some error is being happened and for this, we are gonna take the advantage of get h() function.

This is the app.component.html file. I marked every place where the change took place. At first, the submit function is being introduced inside the form.

Let’s introduced something new in this blog. I already provided you the code and even marked the changes on it. I want you to figure out what is happening by that code and why we need that.

I hope you will enjoy this task.

--

--