Angular Reactive Forms - Validation on Submit
Built with Angular 14.2 and Reactive Forms
This is a quick example of how to trigger form validation on submit with Reactive Forms in Angular.
By default form validation messages are displayed on input fields as soon as they are edited (a.k.a. touched or dirty).
The example code is a simple registration form from an Angular validation tutorial I posted recently, for more info and a live demo see Angular 14 - Reactive Forms Validation Example.
Trigger Validation on Form Submit
To setup an Angular Reactive Form to display validation messages on submit instead of on field change, you can add a submitted
boolean property to your Angular component and set it to true
when the form is submitted for the first time, and back to false
with the form is reset.
Then in your Angular component template wrap the validation messages with an *ngIf
directive that only displays when the form is submitted and there are errors (e.g. *ngIf="submitted && f.email.errors"
).
Angular component with form submitted
property
The app component contains our example sign up form which creates the form fields and validators using an Angular FormBuilder
to create an instance of a FormGroup
that is stored in the registerForm
property. The registerForm
is then bound to the <form>
element in the app template below using the [formGroup]
directive.
The submitted
property is declared at the top of the class with the default value of false
. The property is set to true
in the onSubmit()
method and back to false in the onReset()
method.
import { Component, OnInit } from '@angular/core';
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
// import custom validator to validate that password and confirm password fields match
import { MustMatch } from './_helpers';
@Component({ selector: 'app-root', templateUrl: 'app.component.html' })
export class AppComponent implements OnInit {
registerForm!: FormGroup;
submitted = false;
constructor(private formBuilder: FormBuilder) { }
ngOnInit() {
this.registerForm = this.formBuilder.group({
title: ['', Validators.required],
firstName: ['', Validators.required],
lastName: ['', Validators.required],
// validates date format yyyy-mm-dd
dob: ['', [Validators.required, Validators.pattern(/^\d{4}\-(0[1-9]|1[012])\-(0[1-9]|[12][0-9]|3[01])$/)]],
email: ['', [Validators.required, Validators.email]],
password: ['', [Validators.required, Validators.minLength(6)]],
confirmPassword: ['', Validators.required],
acceptTerms: [false, Validators.requiredTrue]
}, {
validators: MustMatch('password', 'confirmPassword')
});
}
// convenience getter for easy access to form fields
get f() { return this.registerForm.controls; }
onSubmit() {
this.submitted = true;
// stop here if form is invalid
if (this.registerForm.invalid) {
return;
}
// display form values on success
alert('SUCCESS!! :-)\n\n' + JSON.stringify(this.registerForm.value, null, 4));
}
onReset() {
this.submitted = false;
this.registerForm.reset();
}
}
Angular template that displays errors on submit
The app component template contains all the html markup for displaying the example registration form. The form element uses the [formGroup]
Angular directive to bind to the registerForm
FormGroup in the app component above.
The form submit event is bound to the onSubmit()
handler method using the Angular event binding (ngSubmit)="onSubmit()"
. Validation messages are displayed only after the user attempts to submit the form for the first time, this is controlled with the submitted
property of the app component.
The cancel button click event is bound to the onReset()
handler in the app component using the Angular event binding (click)="onReset()"
. On form reset all fields are cleared, validation messages are removed and the submitted
property is set back to false
.
<!-- main app container -->
<div class="card m-3">
<h5 class="card-header">Angular 14 Reactive Form Validation</h5>
<div class="card-body">
<form [formGroup]="registerForm" (ngSubmit)="onSubmit()">
<div class="row">
<div class="col mb-3">
<label class="form-label">Title</label>
<select formControlName="title" class="form-control" [ngClass]="{ 'is-invalid': submitted && f.title.errors }">
<option value=""></option>
<option value="Mr">Mr</option>
<option value="Mrs">Mrs</option>
<option value="Miss">Miss</option>
<option value="Ms">Ms</option>
</select>
<div *ngIf="submitted && f.title.errors" class="invalid-feedback">
<div *ngIf="f.title.errors.required">Title is required</div>
</div>
</div>
<div class="col-5 mb-3">
<label class="form-label">First Name</label>
<input type="text" formControlName="firstName" class="form-control" [ngClass]="{ 'is-invalid': submitted && f.firstName.errors }" />
<div *ngIf="submitted && f.firstName.errors" class="invalid-feedback">
<div *ngIf="f.firstName.errors.required">First Name is required</div>
</div>
</div>
<div class="col-5 mb-3">
<label class="form-label">Last Name</label>
<input type="text" formControlName="lastName" class="form-control" [ngClass]="{ 'is-invalid': submitted && f.lastName.errors }" />
<div *ngIf="submitted && f.lastName.errors" class="invalid-feedback">
<div *ngIf="f.lastName.errors.required">Last Name is required</div>
</div>
</div>
</div>
<div class="row">
<div class="col mb-3">
<label class="form-label">Date of Birth</label>
<input type="date" formControlName="dob" class="form-control" [ngClass]="{ 'is-invalid': submitted && f.dob.errors }" />
<div *ngIf="submitted && f.dob.errors" class="invalid-feedback">
<div *ngIf="f.dob.errors.required">Date of Birth is required</div>
<div *ngIf="f.dob.errors.pattern">Date of Birth must be a valid date in the format YYYY-MM-DD</div>
</div>
</div>
<div class="col mb-3">
<label class="form-label">Email</label>
<input type="text" formControlName="email" class="form-control" [ngClass]="{ 'is-invalid': submitted && f.email.errors }" />
<div *ngIf="submitted && f.email.errors" class="invalid-feedback">
<div *ngIf="f.email.errors.required">Email is required</div>
<div *ngIf="f.email.errors.email">Email must be a valid email address</div>
</div>
</div>
</div>
<div class="row">
<div class="col mb-3">
<label class="form-label">Password</label>
<input type="password" formControlName="password" class="form-control" [ngClass]="{ 'is-invalid': submitted && f.password.errors }" />
<div *ngIf="submitted && f.password.errors" class="invalid-feedback">
<div *ngIf="f.password.errors.required">Password is required</div>
<div *ngIf="f.password.errors.minlength">Password must be at least 6 characters</div>
</div>
</div>
<div class="col mb-3">
<label class="form-label">Confirm Password</label>
<input type="password" formControlName="confirmPassword" class="form-control" [ngClass]="{ 'is-invalid': submitted && f.confirmPassword.errors }" />
<div *ngIf="submitted && f.confirmPassword.errors" class="invalid-feedback">
<div *ngIf="f.confirmPassword.errors.required">Confirm Password is required</div>
<div *ngIf="f.confirmPassword.errors.mustMatch">Passwords must match</div>
</div>
</div>
</div>
<div class="form-check mb-3">
<input type="checkbox" formControlName="acceptTerms" id="acceptTerms" class="form-check-input" [ngClass]="{ 'is-invalid': submitted && f.acceptTerms.errors }" />
<label for="acceptTerms" class="form-check-label">Accept Terms & Conditions</label>
<div *ngIf="submitted && f.acceptTerms.errors" class="invalid-feedback">Accept Ts & Cs is required</div>
</div>
<div class="text-center">
<button class="btn btn-primary me-2">Register</button>
<button class="btn btn-secondary" type="reset" (click)="onReset()">Cancel</button>
</div>
</form>
</div>
</div>
Need Some Angular Help?
Search fiverr for freelance Angular developers.
Follow me for updates
When I'm not coding...
Me and Tina are on a motorcycle adventure around Australia.
Come along for the ride!