Learn Angular with free step by step angular tutorials

All you need to learn about Angular, the best tips and free code examples so you can get the most out of Angular.

Angular Forms and Validations

Updated on October 28, 2021
Angular Forms and Validations 30 Min READ! Angular MaterialBeginnersForm ValidationsFormsAngular CLI

Create and Validate Forms in Angular

In this Angular Forms Tutorial, we will cover all the topics you need to know when building Forms in Angular applications. Also, we will mention best practices to design user-friendly forms and validations using Angular and Material Design.

For this forms tutorial we built an example app (that you can download for free) with different inputs and validations to help you understand everything about data collection using angular reactive forms. What are reactive forms? Don't worry, we will also see the differences between template driven forms and reactive forms.

We will cover both basic and advanced input validations such as: how to validate emails in angular, passwords, phone numbers and unique usernames.

We wanted to create the most complete angular forms tutorial, so we also added advanced custom validators.

If this is your first time using angular reactive forms, this tutorial will help you understand the core concepts and fundamentals of angular FormControl, angular FormGroup, angular FormBuilder, the different types of built-in validators and how to create your custom validators.

Angular Form Inputs

Forms are almost always present in any website or application. Forms can be used to perform countless data-entry tasks such as: authentication, order submission or a profile creation.

Building easy-to-use forms requires design and user experience skills, as well as a framework with support for two-way data binding, change tracking, validation, and error handling such as Angular.

If you need to go deeper about angular main building blocks as well as the best practices for building a complete app with Angular you can check Learn Angular from scratch step by step.

Let's keep in mind that the real goal of forms is gathering data. As it always happens in software, there are many different ways of sending and handling data in your server. This depends a lot on the backend and the nature of your business logic, so we are not going to cover this topic in this tutorial. However, if you want to use the MEAN stack (Mongo, Express, Angular and Node), check Building a MEAN stack application.

You can download the source code of this Angular Forms Starter App by clicking the GET THE CODE button from the beginning of the page. Also feel free to check the online demo.

Without further introductions, let's start with our inputs and validations in angular journey!

Angular Forms Fundamentals

Template Driven Forms vs Angular Reactive Forms

Angular offers two form-building technologies: reactive forms and template-driven forms. These two belong to the @angular/forms library and share a series of form control classes. However, they notably diverge in terms of philosophy, programming style and technique. They even have their own modules: ReactiveFormsModule and FormsModule.

Reactive forms differ from template-driven forms in distinct ways. Reactive forms provide synchronous access to the data model, immutability with observable operators, and change tracking through observable streams.

Template-driven forms let direct access modify data in your template, but are less explicit than reactive forms because they rely on directives embedded in the template, along with mutable data to track changes asynchronously

Before we continue with our example app using angular reactive forms, let's clarify what are the main differences between Reactive Forms and Template-driven Forms from a high level perspective. The following table is extracted from the Angular documentation.

Reactive Forms Template-driven Forms
Setup of form model Explicit, created in component class Implicit, created by directives
Data model Structured and immutable Unstructured and mutable
Data flow Synchronous Asynchronous
Form validation Functions Directives

Angular Reactive Forms

Angular reactive forms help the reactive programming style that favors an explicit data management flow between non-UI data models (typically retrieved from a server) and a UI-oriented form model that keeps the states and values of HTML controls on the app screen.

They offer an easy way to use reactive patterns, testings and validations.

When using reactive forms (also known as model-driven forms), we will avoid directives like required, ngModel, NgForm and such. The idea is that instead of declaring that, we actually use the underlying APIs to do it for us and so make Angular power things for us. In a sense, instead of binding Object models to directives like in template-driven forms, we create our own instances inside a component class and construct our very own JavaScript models.

This approach has a lot more power and is extremely productive to work with since it allows us to write expressive code that is very testable and keeps all logic in the same place, instead of dividing it around different form templates.

With reactive forms, you are able to create and manipulate form control objects directly in the Component. Since the component class has access to the form control structure and to the data model, you can push data model values into the form controls and pull values that have been changed by the user back out. The component is able to observe changes in the form control state and react to these changes, for example to show a validation message.

One of the advantages of working directly with form control objects is that value and validity updates are always synchronous and under your control. You won't find the timing issues that sometimes affect a template-driven form. Also, reactive forms can be easier to unit test.

Angular Template driven forms

On the other hand, Template-driven forms take a completely different approach.

You can place HTML form controls (such as <input> or <select>) in the component template and bind them to data model properties in the component, using directives like ngModel.

In template-driven forms, you don't create Angular form control objects. Angular directives create them for you using information from your data bindings. You don't have to push and pull data values because Angular handles that for you through the ngModel directive. Angular updates the mutable data model according to user changes as they happen.

Examples of these directives are the ngModel, required, maxlength and so. On an advanced level, this is what template-driven forms achieve for us — by specifying directives to bind our models, values, validations and more, we are letting the template do all the work on the background.

While this approach means less code in the component class, the template-driven forms are asynchronous which may complicate the development in more advanced scenarios.

Template driven forms may resemble more as how forms used to be in AngularJS (v 1.0), probably, that's why people still use them as a familiar option.

Which one is the best? Reactive or template-driven?

Neither of them is betten than the other. They're two different architectural paradigms with their own pros and cons. You can choose the approach that works best for you. This way you are free to decide to use both in the same application.

In this Angular forms tutorial we will use Reactive Forms.

Common form foundation classes. FormGroup, FormControl, FormArray and FormBuilder

There are some basic concepts you need to grasp before we start.

Both reactive and template-driven forms track value changes between the form input elements that users interact with and the form data in your component model. The two approaches share underlying building blocks, but differ in how you create and manage the common form-control instances.

Both types of forms are built on the following base classes:

FormControl: it tracks the value and validity status of an angular individual Angular form control. It matches an HTML form control such as an input or a selector.

This basic example shows a FormControl for the username property which should not be empty.

this.username = new FormControl('Dayana', Validators.required);
<ion-input type="text" formControlName="username"></ion-input>

FormGroup: it tracks the value and validity state of a FormBuilder instance group. It aggregates the values of each child FormControl into one object, using the name of each form control as the key. Furthermore, it calculates its status by reducing the statuses of its children. For example, if one of the controls in a group is invalid, the entire group becomes invalid.

this.user = new FormGroup({
   username: new FormControl('Dayana', Validators.required),
   city: new FormControl('Montevideo', Validators.required)

FormArray: it is a variation of FormGroup. The main difference is that its data gets serialized as an array, as opposed to being serialized as an object in case of FormGroup. This might be especially useful when you don't know how many controls will be present within the group, like in dynamic forms.

this.user = new FormArray({
   new FormControl('agustin', Validators.required),
   new FormControl('Montevideo', Validators.required)

FormBuilder: is a helper class that creates FormGroup, FormControl and FormArray instances for us. It basically reduces the repetition and clutter by handling details of form control creation for you.

this.user = this.formBuilder.group({
	username: new FormControl('', Validators.required),
	email: new FormControl('', Validators.compose([

All of these components should be imported from the @angular/forms module.

import { Validators, FormBuilder, FormGroup, FormControl, FormArray } from '@angular/forms';
Angular is packed with its own input validators. In this angular forms tutorial, we will also explain how to use many of Angular's built-in validators and also how to create custom ones. This way we can improve the user experience and make our app significantly better for users.

Forms UX to increase conversions

Forms are also a bottleneck of the conversion funnel, because they require the user to perform a task much more complex than just a click, by thinking and typing all the required information by the form. This may be frustrating when you type all the information and, in the end, you need to retype all over again, which is why you have to strive to do your best to have an awesome user experience to reduce users frustrations and conclusively improve your funnel conversion.

Some people say that user experience is often more important than what the app offers itself. So before we start coding, it's important to take some time to analyze which fields are really needed. When designing the mock up, always try to include the fields that are essential to you, knowing that the bigger the form, the bigger chance you have of losing some users.

Keep in mind that simplicity is well appreciated, ask only for what you precisely need.

From a design perspective, Angular Material has a wide collection of nice form elements and form inputs for you to choose. From the UX point of view we will make use of angular form validations to enhance the experience of our angular website app.

Best practices say you should always validate all user inputted data via backend and we agree with that statement. However, by also validating via frontend, you can improve the user experience and perceived response time.

We have a specific tutorial Angular Forms best practices where you can learn more about form structure, help blocks, input types, validations, accessibility, and more of the things you need to consider when building an Angular Forms in order to provide a good UX.

When should we validate our forms?

The most important thing is to avoid interrupting and annoying the user. That's why it's not recommended to validate right just when the user submits the form. Imagine this scenario, the user spent time filling each input (without knowing the constraints beforehand) and finally when he thinks the task is done, multiple error messages are shown due to invalid inputs. This is frustrating.

Instead, we should consider these options:

Real time validation: It means validating as you type. These are super handy and are the default for Angular Validators.

On blur validation: It may be less annoying for certain cases as we only validate when the user focus out from the form input. However, the user will only see the errors once he moves to the next input. As we can see in the official documentation Angular has a new option for your ngModel updateOn: 'blur' and you can use it like this:

this.email = new FormControl(null, {
	validators: Validators.required,
	updateOn: 'blur'

Angular Material for the win

Angular Material is a project developed by Google which goal is to build a set of high-quality UI components built with Angular and TypeScript, following the Material Design specifications. These components serve as an example of how to write Angular code following best practices. You can use these components in your angular apps very easily.

In our forms and validation example project we are using the following Material components: form fields, inputs, date picker, checkbox, select and buttons.

Designing our form and validation requirements

The goal of this tutorial is to explain how to master angular form validations so we will be going through many form validation examples. It's important to take some time to write down your form and validation requirements before starting. Below, we will show the data we want to collect with its corresponding constraints.

Angular Forms and Validations example

For this angular forms and validations example we created two separate forms: user details and account details.

User details form components and constraints:

Input Name Input Type Input Validations Validation Type
Bio Text area 1. can't be more than 256 characters long 1. Maxlength 256
Birthday Date picker 1. not empty 1. required
Gender Select 1. not empty 1. required
Country Select 1. not empty 1. required
Phone Telephone 1. not empty
2. valid for the selected country
1. required
2. custom validation
Full Name Text 1. not empty 1. required

Account details form components and constraints:

Input Name Input Type Input Validations Validation Type
User Name Text 1. not empty
2. at least 5 characters long
3. can't be more than 25 characters long
4. must contain only numbers and letters
5. unique in our system
1. required
2. minlength 5
3. maxlength 25
4. pattern validation
5. custom validation
Email email 1. not empty
2. valid email
1. required
2. pattern validation
Password password 1. not empty
2. at least 5 characters long
3. must contain at least one uppercase,
one lowercase, and one number
1. required
2. minlength 5
3. pattern validation
Confirm password password 1. not empty
2. Equal to password
1. required
2. custom validation
Terms checkbox 1. accepted 1. pattern validation

In Fully - Angular Admin Template we have lots of different inputs and validations that you can use in your angular project. There are basic and also advanced forms and validations that you should definitely check in Fully - Angular Admin Template.

Angular forms

Angular Forms and Validations example

Let's put in practice what we have learned so far and start building our Angular form handling and validations example app. The following image is how our final Angular example will look like.

Angular Forms and Validations example

In reactive forms, instead of adding validators through attributes in the template (like it happens with template driven forms), you add validator functions directly to the form control model in the angular component class. Angular will call these functions whenever the value of the control changes.

You can choose between writing your own validator functions and using some of the Angular built-in validators.

Built-in validators are stock validators provided by Angular. For a full list of Angular built-in validators, here you can see the Validators API reference. However, built-in validators won't always match the exact use case of your application, so sometimes you will want to create a custom validator for your angular website.

Built in Angular input validations

We will use the following Angular built-in validators to validate our form inputs:

minLength: Validator that requires controls to have a value of a minimum length.

maxLength: Validator that requires controls to have a value of a maximum length.

pattern: Validator that requires a control to match a regex to its value. You can find more information about regex patterns in the PatternValidator reference.

email: Validator that performs email validation.

compose: is used when more than one validation is needed for the same form field.

required: Validator that requires controls to have a non-empty value. It also validates that the value matches the input type. For example, if the input is of “email” type, then the input will be valid if it's not empty and if the value is of email type.

Let's start with some name and email inputs validations. These are the requirements for our form controls:

  • Full name: required
  • Email: valid email and required
  • Terms: must accept terms and conditions (checkbox checked validation)

We will define our User Details Form with a FormGroup. To create the FormGroup we will use an instance of the FormBuilder. Check the following typescript code to see how you can achieve this. Note that for the full name and bio we preloaded some default data. You can find this code in src/app/form-component/form.component.ts

// user details form validations
this.userDetailsForm = this.fb.group({
	 fullname: ['Homero Simpson', Validators.required ],
	 bio: ["Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s", Validators.maxLength(256)],
	 birthday: ['', Validators.required],
	 gender: new FormControl(this.genders[0], Validators.required),
	 country_phone: this.country_phone_group

Now we have to create the form, inputs and their error messages inside our template file. Below is the html code for defining the form and the name input. Download the source code (click the GET THE CODE button from the beginning of this page) of this Angular forms and validations example to see all the inputs. We didn't put it here because we don't want to make this tutorial super large.

You can find this code in src/app/form-component/form.component.html

<form [formGroup]="userDetailsForm" (ngSubmit)="onSubmitUserDetails(userDetailsForm.value)">
    <mat-form-field class="full-width">
      <input matInput placeholder="Full Name" formControlName="fullname" required>
      <mat-error *ngFor="let validation of validation_messages.fullname">
        <mat-error class="error-message" *ngIf="userDetailsForm.get('fullname').hasError(validation.type) && (userDetailsForm.get('fullname').dirty || userDetailsForm.get('fullname').touched)">{{validation.message}}</mat-error>
	<button class="submit-btn" color="primary" mat-raised-button type="submit" [disabled]="!userDetailsForm.valid">Submit</button>

All the mat tags are components from angular material like mat-error which is the way to display the error messages in our mat-form-field.

<mat-form-field> is a component used to wrap several Angular Material components and apply common styles such as underline, floating label, and hint messages. Read more in the official documentation.

Angular email validation example

To validate the email we have to use a pattern type validator. The email validation pattern we are going to use is ^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+.[a-zA-Z0-9-.]+$

So we have to create our FormGroup like we explained above and create a FormControl to define our email form control with its validations.

this.accountDetailsForm = this.fb.group({
	email: new FormControl('', Validators.compose([
	// more form inputs

Then, in the template file, the html looks just like the other inputs code with a mat-form-field enclosing the input and the mat-error.

<mat-form-field class="full-width">
  <input matInput type="email" placeholder="Email" formControlName="email" required>
  <mat-error *ngFor="let validation of account_validation_messages.email">
    <mat-error class="error-message" *ngIf="accountDetailsForm.get('email').hasError(validation.type) && (accountDetailsForm.get('email').dirty || accountDetailsForm.get('email').touched)">{{validation.message}}</mat-error>

I forgot to mention that we have defined our error messages also in our src/app/form-component/form.component.ts file. Each input can have more than one validation that's why we created an array of validation messages for each form input.

For example for our Account Details form we have the following error messages:

account_validation_messages = {
'username': [
  { type: 'required', message: 'Username is required' },
  { type: 'minlength', message: 'Username must be at least 5 characters long' },
  { type: 'maxlength', message: 'Username cannot be more than 25 characters long' },
  { type: 'pattern', message: 'Your username must contain only numbers and letters' },
  { type: 'validUsername', message: 'Your username has already been taken' }
'email': [
  { type: 'required', message: 'Email is required' },
  { type: 'pattern', message: 'Enter a valid email' }
'confirm_password': [
  { type: 'required', message: 'Confirm password is required' },
  { type: 'areEqual', message: 'Password mismatch' }
'password': [
  { type: 'required', message: 'Password is required' },
  { type: 'minlength', message: 'Password must be at least 5 characters long' },
  { type: 'pattern', message: 'Your password must contain at least one uppercase, one lowercase, and one number' }
'terms': [
  { type: 'pattern', message: 'You must accept terms and conditions' }

Then in our mat-error we iterate through the error messages of each specific input. Let's go back to the email validation example. We iterate the account_validation_messages.email messages and for each of them we show it only if the form control has an error of that type and the control is dirty or touched. What does that mean? It means that the user has already touched the input.

<mat-error *ngFor="let validation of account_validation_messages.email">
	<mat-error class="error-message" *ngIf="accountDetailsForm.get('email').hasError(validation.type) && (accountDetailsForm.get('email').dirty || accountDetailsForm.get('email').touched)">{{validation.message}}

Advanced angular form controls and custom validators

We can get very creative and build any kind of custom validators. In this tutorial we will be implementing three in particular.

  • Validate that the username is unique (checks against a fake server if the username is already taken).
  • Validate that the confirm password input value is equal to the password input value.
  • Validate if the phone is valid for the selected country.

Angular custom validator example for the username

Let's start with the easiest one. The goal of this custom angular validator is to validate if a username is available or not. This simple implementation doesn't allows the username to be abc123 or 123abc. We don't have a dedicated backend and database for this angular app example so we hardcoded two fake existing usernames just to show you how to achieve this validator.

Note that in a real application you will have to check this validator against your username database.

We added the following code in a new username.validator.ts file:

import { FormControl } from '@angular/forms';
export class UsernameValidator {
  static validUsername(fc: FormControl){
    if(fc.value.toLowerCase() === "abc123" || fc.value.toLowerCase() === "123abc"){
      return ({validUsername: true});
    } else {
      return (null);

With fc.value we obtain the value in the username field, so we can control the usernames we won't allow to enter.

Then, in the typescript file where we have defined our form, we should import our custom validator.

import { UsernameValidator } from '../../validators/username.validator';

this.validations_form = this.formBuilder.group({
  username: new FormControl('', Validators.compose([

We used minLength(5) and maxLength(25) to ensure the minimum and maximum length of the value. We also used required to avoid this input to be left empty, and ng-pattern="^(?=.*[a-zA-Z])(?=.*[0-9])[a-zA-Z0-9]+$" to force a correct input value containing letters and numbers.

Angular custom validator for a phone number

The goal of this custom angular validator is to validate that a phone number is valid for a certain country.

We will use Google Libphonenumber JavaScript library for parsing, formatting, and validating international phone numbers. To install it you should run:

npm install --save-prod google-libphonenumber

We added the following code in a new phone.validator.ts file to create our custom phone validator:

import { AbstractControl, ValidatorFn } from '@angular/forms';
import * as libphonenumber from 'google-libphonenumber';

export class PhoneValidator {

  // Inspired on: https://github.com/yuyang041060120/ng2-validation/blob/master/src/equal-to/validator.ts
  static validCountryPhone = (countryControl: AbstractControl): ValidatorFn => {
    let subscribe = false;
    return (phoneControl: AbstractControl): {[key: string]: boolean} => {

      if (!subscribe) {
        subscribe = true;
        countryControl.valueChanges.subscribe(() => {

      if (phoneControl.value !== '') {
        try {

          const phoneUtil = libphonenumber.PhoneNumberUtil.getInstance();
          const phoneNumber = '' + phoneControl.value + '';
          const region = countryControl.value;
          const pNumber = phoneUtil.parseAndKeepRawInput(phoneNumber, region.iso);
          const isValidNumber = phoneUtil.isValidNumber(pNumber);

          if (isValidNumber) {
            return undefined;
        } catch (e) {
          return {
            validCountryPhone: true

        return {
          validCountryPhone: true
      } else {
        return undefined;

The phone directive controls that the value from the phone number input is correct for the selected country.

Then, in the typescript file where we have defined our form (form.ts), we should import our custom validator. In form.ts we have a FormGroup with the phone input and the country selector so when the value changes in one of these fields the directive checks if both are correct.

this.country_phone_group = new FormGroup({
		country: new FormControl(this.countries[0], Validators.required),
		phone: new FormControl('', Validators.compose([

Angular password validation

Let's continue with the password form control validation. As mentioned before, we have the following requirements for the password input form:

  • Required
  • Min length (5)
  • Must contain letters (both uppercase and lowercase) and numbers
  • User must re-type password to ensure correctness

We added the following code in a new password.validator.ts file to create our PasswordValidator which validates that the password was re-typed correctly. It's like a password match validator.

import { FormControl, FormGroup, NgForm, FormGroupDirective } from '@angular/forms';

export class PasswordValidator {
  // Inspired on: http://plnkr.co/edit/Zcbg2T3tOxYmhxs7vaAm?p=preview
  static areEqual(formGroup: FormGroup) {
    let value;
    let valid = true;
    for (let key in formGroup.controls) {
      if (formGroup.controls.hasOwnProperty(key)) {
        let control: FormControl = <FormControl>formGroup.controls[key];

        if (value === undefined) {
          value = control.value
        } else {
          if (value !== control.value) {
            valid = false;

    if (valid) {
      return null;

    return {
      areEqual: true

Then, in the typescript file where we have defined our form (form.ts), we should import our custom password validator and add the other simple validations: password no shorter than 5 chars, and with letters and numbers.

import { PasswordValidator } from '../../validators/password.validator';
this.matching_passwords_group = new FormGroup({
	password: new FormControl('', Validators.compose([
	 	Validators.pattern('^(?=.*[a-z])(?=.*[A-Z])(?=.*[0-9])[a-zA-Z0-9]+$') //this is for the letters (both uppercase and lowercase) and numbers validation
	confirm_password: new FormControl('', Validators.required)
}, (formGroup: FormGroup) => {
	 return PasswordValidator.areEqual(formGroup);

And then in for the html of this password inputs we do the following:

<div formGroupName="matching_passwords">
  <mat-form-field class="full-width">
    <input matInput type="password" placeholder="Password" formControlName="password" required>
    <mat-error *ngFor="let validation of account_validation_messages.password">
      <mat-error class="error-message" *ngIf="accountDetailsForm.get('matching_passwords').get('password').hasError(validation.type) && (accountDetailsForm.get('matching_passwords').get('password').dirty || accountDetailsForm.get('matching_passwords').get('password').touched)">{{validation.message}}</mat-error>

  <mat-form-field class="full-width">
    <input matInput type="password" placeholder="Confirm Password" formControlName="confirm_password"  [errorStateMatcher]="parentErrorStateMatcher" required>
    <mat-error *ngFor="let validation of account_validation_messages.confirm_password">
      <mat-error class="error-message" *ngIf="(accountDetailsForm.get('matching_passwords').get('confirm_password').hasError(validation.type)|| accountDetailsForm.get('matching_passwords').hasError(validation.type)) && (accountDetailsForm.get('matching_passwords').get('confirm_password').dirty || accountDetailsForm.get('matching_passwords').get('confirm_password').touched)">{{validation.message}}</mat-error>


Noticed the error state matcher in the confirm password input? We created a ParentErrorStateMatcher which implements from ErrorStateMatcher and its goal is the validate that the form group is valid. In this case the form group includes all the password input validations and the confirm password input validations.

Keep learning Angular

In this Angular forms and validations tutorial you learned how to handle forms in angular and also how to validate your form inputs. Now that you've experienced more about data collection using forms and some techniques to create your custom validators, you can continue with:

Hopefully, you didn't run into any issues with these Angular Forms and Validations examples, but if you did, feel free to post in the comments section below. Remember you can get the full source code of this Angular app by clicking the GET THE CODE button from the beginning of this page.

If you want to build a complex and robust web app with Angular you should check Fully - Angular Admin Template which is the most complete and advanced Angular Template with lots of components and performance improvements. It includes features such as Angular Universal, AOT (ahead of time compilation), Material Design, Lazy Loading Routes and lots of useful and responsive components.

Angular Admin Template


Our aim is to help developers of different skill levels get the most out of Angular by saving their expensive time and providing great resources for them to learn faster and better.