Published: April 06 2023

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 &&").

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 ={
            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,]],
            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) {

        // display form values on success
        alert('SUCCESS!! :-)\n\n' + JSON.stringify(this.registerForm.value, null, 4));

    onReset() {
        this.submitted = false;

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>
                    <div *ngIf="submitted && f.title.errors" class="invalid-feedback">
                        <div *ngIf="f.title.errors.required">Title is required</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 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 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 class="col mb-3">
                    <label class="form-label">Email</label>
                    <input type="text" formControlName="email" class="form-control" [ngClass]="{ 'is-invalid': submitted && }" />
                    <div *ngIf="submitted &&" class="invalid-feedback">
                        <div *ngIf="">Email is required</div>
                        <div *ngIf="">Email must be a valid email address</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 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 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 class="text-center">
                <button class="btn btn-primary me-2">Register</button>
                <button class="btn btn-secondary" type="reset" (click)="onReset()">Cancel</button>


Need Some Angular Help?

Search fiverr for freelance Angular developers.

Follow me for updates

On Twitter or RSS.

When I'm not coding...

Me and Tina are on a motorcycle adventure around Australia.
Come along for the ride!


Supported by