Angular
Intro to Angular Reactive Forms
Explore Angular reactive forms by building a simple form step by step
Angular offers two main approaches to handling user input through forms: reactive and template-driven. Both approaches build on top of a common Forms API.
In this post, I will build a form following the reactive approach, also known as model-driven forms.

I wrote an Intro To Angular Template-driven Forms, where I built the same form using the template-driven approach.
I found it useful to build the same form using the two approaches to appreciate the differences better.
Overview of Reactive Approach
According to the documentation, reactive forms “Provide direct, explicit access to the underlying forms object model.
If forms are a key part of your application, or you’re already using reactive patterns for building your application, use reactive forms.”
Furthermore, they add that reactive forms are more robust than template-driven forms: they’re more scalable, reusable, and testable.
Don’t take this as written in stone
The debate over the best approach may never be resolved.
Do you prefer template-driven forms or reactive forms?
In The Angular Plus Show podcast, they gave space to Ward Bell, a Google developer expert in Angular and president/co-founder at IdeaBlade.
Ward Bell has been using template-driven forms for years and is one of the best specialists on the topic.
Make sure you listen to the episode to form (lol) your opinion.
FormsModule and Two Directives
First of all, we need to remember to import ReactiveFormsModule because it “exports the required infrastructure and directives for reactive forms.”
Therefore, we import the ReactiveFormsModule
in app.module.ts
.
import { ReactiveFormsModule } from '@angular/forms';
and declare it in the imports in @NgModule.
imports: [BrowserModule, FormsModule, ReactiveFormsModule],
Note that FormsModule
is there because the same application uses a template-driven form in another component.
If we require both, we should import them both.
However, FormsModule
is important because both NgModel
and NgForm
directives are exported from FormsModule
.
Building a Form Element
In theory, we could start from either the class or the template.
However, “Reactive forms provide a model-driven approach to handling form inputs whose values change over time.”
In reactive forms, it is more natural to start from the class, even though some people may prefer to start from the template, and that is totally fine.
Since Intro To Angular Template-Driven Forms, I started with a generic form element; I will follow that approach to show that some things are the same.
A Generic Form Element
A generic form element in Angular forms may look like the following:
<div>
<label for="email">Email</label>
<input type="email" id="email" [formControl]="email" />
</div>
Once again, this is pretty much plain HTML, except for [formControl]=”email"
.
The formControl
binding comes from the FormControlDirective
, which comes from the ReactiveFormsModule
that we imported above.
If you are familiar with the Angular syntax, this isn’t new as it looks similar to property binding.
What does it bind to?
It binds to the email property in the class.
After importing FormControl
, we can assign a new FormControl
instance to email
. FormControl
“Tracks the value and validation status of an individual form control,” angular.io.
import { Component } from '@angular/core';
import { FormControl } from '@angular/forms';...
export class ReactiveFormComponent {
email = new FormControl('');
}
In this case, by using new FormControl('')
we set the initial value of email
to an empty string.
Thanks to FormControl
, we can listen for, update, and validate the state of the form element.
You get all the benefits of two-way binding and more, like validations.
We will get to validations soon.
Display FormControl values
You can easily display the value by using interpolation and the value
property in the template as follows:
<p>Value: {{ email.value }}</p>
A second way is “Through the valueChanges
observable where you can listen for changes in the form’s value in the template using AsyncPipe or in the component class using the subscribe() method”, angular.io.
From One Element to a Form
Starting from the generic element above, we can create the following form:
import { Component } from '@angular/core';
import { FormGroup, FormControl } from '@angular/forms';...
reactiveForm = new FormGroup({
name: new FormControl(''),
email: new FormControl(''),
age: new FormControl(''),
});
Note that we must import FormGroup
from @angular/forms in every component where we want to create a new FormGroup
instance.
We created a FormGroup
instance in the class. FormGroup “Tracks the value and validity state of a group of FormControl instances,” angular.io.
We then need to associate the FormGroup
model and view inside the template using property binding.
As for template-driven forms, we want to have a way to work with the form as a whole rather than dealing with each element.
First difference
We can see the first difference with template-driven forms in the form
tag. We are not using a reference variable anymore.
Second difference
A second difference consists of formControlName
.
“The formControlName
input provided by the FormControlName
directive binds each individual input to the form control defined in FormGroup
,” angular.io.
However, the form group instance provides the source of truth for the model value.
Third difference
A third difference is that we don’t need to use the name attribute in the input tags.
Your app wouldn’t crash, but you would get an ugly-looking error in your console.

As a side note, you can also group controls into a single form by using a Form Array. We won’t discuss this in this post.
Validation
At the moment, we have no validation.
Reactive forms handle validation through special functions called validators. Angular provides built-in validators that you can use off the shelf.
The easiest way to use validators is by passing them as the second parameter to FormControl
.
reactiveForm = new FormGroup({
name: new FormControl('', Validators.required),
email: new FormControl(''),
age: new FormControl('', [
Validators.required,
Validators.max(99),
Validators.min(18),
]),
});
In the code snippet, I added two validators. The first one makes name
required. The second validator check that age
is between 18 and 99.
By updating the submit button as follows:
<button type="submit" [disabled]="reactiveForm.invalid">Submit</button>
we make sure that the button is active only when the form is valid, e.g., the conditions in the validators are met.
When the built-in validators aren’t enough, we can even create custom validators for more complex cases.
Quick Summary
Angular offers two main approaches to building forms: reactive and template-driven. In this post, we explored the reactive approach.
Both approaches build on top of a common Forms API.
- Import
ReactiveFormsModule
inapp.module.ts
- Use
new FormControl()
to instantiate a form control - Use
new FormGroup()
to create a group of form controls - Bind the
FormGroup
model in the class with the view through property binding[FormGroup]="myFormGroupName"
- The
<form>
tag implementNgForm
by default after importingReactiveFormsModule
Feel free to take a look at the code on GitHub.