Angular provides two ways of building and managing forms: template-driven and reactive.
Template-driven forms are created primarily in a component’s template (minimal code in the component’s class). In contrast, reactive forms are created mainly in a component’s class (minimal code in the component’s template).
Choosing which type is best depends on the following:
<!-- template-driven --><form #userForm="ngForm"><label for="name">Name:</label><input id="name" type="text" name="name" required [(ngModel)]="user.name"></form>
// reactiveimport { FormGroup, FormControl, Validators } from '@angular/forms';export class UserComponent {userForm = new FormGroup({name: new FormControl('', Validators.required)});// rest of class}
In Angular, template-driven forms are created primarily in the template using the FormsModule
(@angular/forms
) and HTML <form>
and <input>
elements.
The FormsModule
provides the NgForm
and NgModel
directives to manage a form. The FormsModule
automatically attaches a NgForm
directive to <form>
elements in the template, which manages the overall form state.
The NgModel
directive is two-way bound to an <input>
(form control) element to “connect” it to a data model in the component. This data model is updated with any changes to the form control value.
<!-- UserComponent template --><form #userForm="ngForm"><label for="name">Name:</label><!-- connected to user model in class --><input id="name" type="text" name="name" [(ngModel)]="user.name" required></form>
In Angular, reactive forms are created primarily in a component’s class using the ReactiveFormsModule
(@angular/forms
) and its utility classes: FormGroup
and FormControl
.
The overall form structure and the initial state are created in the class using FormGroup
and FormControl
. FormGroup
contains the overall form structure and state and maintains a set of FormControl
instances. A FormControl
instance manages the state of a single form control. This form structure is then connected to the template using the formGroup
and formControlName
directives.
// others importsimport { ReactiveFormsModule, FormGroup, FormControl } from "@angular/forms"@Component({// other configurationsimports: [ReactiveFormsModule] // imports reactive forms module})export class UserComponent {// create form structure using FormGroupuserForm = new FormGroup({// create a form control with initial value using FormControlname: new FormControl("")})// rest of class}
In Angular, template-driven forms use the NgForm
and NgModel
directives (@angular/forms
).
The NgForm
directive is automatically applied to <form>
elements by the FormsModule
(when imported into the component) and manages the overall state of the form and its controls.
The NgModel
directive is two-way bound to a data model in the class and should be used with the name
attribute. NgModel
registers the control within the NgForm
instance and manages the state of the form control.
<!-- UserComponent template --><form #userForm="ngForm"> <!-- ngForm instance referenced through template variable --><label for="name">Name:</label><inputid="name"type="text"name="name"[(ngModel)]="user.name" required><!-- name attribute needed for proper registration --><!-- ngModel registers the control within userForm --></form>
In Angular, validation can be added to template-driven form controls using the built-in HTML validation attributes like required
and minlength
. Angular provides more specific validation using directives like email
. Angular automatically picks up the validation and updates the NgModel
state.
Errors can be evaluated using the NgModel
errors
property.
<!-- UserComponent template --><form #userForm="ngForm"><div><label for="name">Name:</label><!-- #name stores NgModel instance in template variable --><!-- required built-in HTML validation --><!-- minlength built-in HTML validation --><inputid="name"type="text"name="name"[(ngModel)]="user.name"requiredminlength="5"#name="ngModel"><!-- conditionally render error message -->@if(name.errors?.['required']) {<span>Name is required!</span>}</div><div><label for="email">Email:</label><!-- required built-in HTML validation --><!-- email Angular directive validation --><!-- #email stores NgModel instance in template variable --><inputid="email"type="email"name="email"[(ngModel)]="user.email"required#email="ngModel"><!-- conditionally render error message -->@if(email.errors?.['email']) {<span>Valid email required!</span>}</div></form>
In Angular, both template-driven and reactive forms have special CSS classes applied to the form control depending on its state (touched, valid, submitted, etc.).
These classes can apply styling to the form controls in specific states, such as valid or invalid.
input.ng-valid.ng-touched {/* apply green border when input is valid and touched */border-color: green;}input.ng-invalid.ng-touched {/* apply red border when input is invalid and touched */border-color: red;}
FormGroup
in AngularIn Angular, FormGroup
(@angular/forms
) is a utility class used to manage a set of FormControl
instances.
A FormGroup
is created using an object where the keys are the names of the form controls and the values are the FormControl
instances.
// others importsimport { ReactiveFormsModule, FormGroup, FormControl, Validators } from "@angular/forms"@Component({// other configurationsimports: [ReactiveFormsModule]})export class UserComponent {// create form structure using FormGroupuserForm = new FormGroup({name: new FormControl("", Validators.required),email: new FormControl("", Validators.email)})// rest of class}
FormGroup
and FormControl
APIIn Angular, the FormGroup
and FormControl
APIs can be used to manage the values, states, and errors of a form control(s).
The FormGroup
API is used to manage the overall form state and errors and to get specific FormControl
instances.
The FormControl
API manages the state, errors, and value of a single FormControl
.
// others importsimport { ReactiveFormsModule, FormGroup, FormControl, Validators } from "@angular/forms"@Component({// other configurationsimports: [ReactiveFormsModule]})export class UserComponent {// create form structure using FormGroupuserForm = new FormGroup({// create FormControl with required validationname: new FormControl("", Validators.required),// create FormControl with email and required validationemail: new FormControl("", [Validators.email, Validators.required])})hasError(controlName: string, errorType: string) {const control = this.userForm.get(controlName)if(control?.valid) {return false}// report value for errorconsole.log(control.value)// access errorreturn control.touched && control.errors?.[errorType]}submitForm() {// check overall form stateif(this.userForm.valid) {// process form valuesconsole.log(this.userForm.value)} else {if(this.userForm.get('name').errors) {// report errors for name}if(this.userForm.get('email').errors) {// reports errors for email}}}// rest of class}
FormGroup
in Reactive Form TemplateWhen working with reactive forms in Angular, the <form>
element needs to be connected to a FormGroup
instance in the template using the formGroup
directive.
formGroup
(from ReactiveFormsModule
) is a property bound to the FormGroup
instance and takes over synchronizing the <form>
element state.
import { ReactiveFormsModule, FormGroup } from "@angular/forms"@Component({// ...imports: [ReactiveFormsModule]})export class UserComponent {userForm = new FormGroup({//FormControls});}
<!-- bind to `FormGroup` --><form [formGroup]="userForm"><!-- controls --></form>
FormControl
in Reactive Form TemplateWhen working with reactive forms in Angular, form controls in the template are connected to their FormControl
instance in an enclosing FormGroup
using the formControlName
directive.
formControlName
(from ReactiveFormsModule
) is added to a form control within a FormGroup
to let the FormControl
take over managing the control state.
<!-- bind to FormGroup --><form [formGroup]="userForm"><label for="name">Name:</label><!-- connects form control to the name FormControl instance --><input id="name" type="text" formControlName="name"></form>
Validators
Class in Reactive FormsThe Validators
class (@angular/forms
) provides validation functions that can be passed to FormControl
instances when creating them.
Some validators like Validators.required
and Validators.email
can be used without configuration, while others like Validators.pattern()
and Validators.minLength()
need parameters to create the validator function.
// others importsimport { ReactiveFormsModule, FormGroup, FormControl, Validators } from "@angular/forms"@Component({// other configurationsimports: [ReactiveFormsModule]})export class UserComponent {// create form structure using FormGroupuserForm = new FormGroup({// create FormControl with required validationname: new FormControl("", Validators.required),// create FormControl with email, required, pattern validationemail: new FormControl("", [Validators.email, Validators.required, Validators.pattern(/^[a-zA-Z0-9._-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/)])})// rest of class}
FormBuilder
Service in AngularThe FormBuilder
service provides a “syntactic sugar” API for creating reactive forms. This service simplifies creating forms by reducing the boilerplate code needed using its .group()
method, which creates a FormGroup
instance.
// others importsimport { ReactiveFormsModule, Validators, FormBuilder } from "@angular/forms";import { Component, inject } from "@angular/core";@Component({// other configurationsimports: [ReactiveFormsModule]})export class UserComponent {formBuilder = inject(FormBuilder) // inject FormBuilder// create form structure using FormGroupuserForm = this.formBuilder.group({name: ['', Validators.required],email: ['', [Validators.required,Validators.email,Validators.pattern(/[^!-]/)]]});// rest of class}
FormControl
in AngularIn Angular, FormControl
(@angular/forms
) is a utility class for managing a single form control.
A FormControl
is created using the initial value of the form control and any Validators
functions to apply.
// others importsimport { ReactiveFormsModule, FormGroup, FormControl, Validators } from "@angular/forms"@Component({// other configurationsimports: [ReactiveFormsModule]})export class UserComponent {// create form structure using FormGroupuserForm = new FormGroup({// create FormControl with required validationname: new FormControl("", Validators.required),// create FormControl with `email` and required validationemail: new FormControl("", [Validators.email, Validators.required])})// rest of class}