DEV Community

Cover image for Angular Form Validation
Ramanan Balamurugan
Ramanan Balamurugan

Posted on

Angular Form Validation

Introduction:
Angular forms play a important role in creating dynamic and interactive web applications. Whether you're building a simple contact form or a complex multi-step wizard, Angular provides powerful features and tools to handle form validation, data binding, and user interactions. In this post, we will explore the everything we need to know about types of forms in angular and it's validations.

Table of Contents:

1. Angular Forms & Types
2. Template-Driven Forms
3. Reactive Forms
4. Advanced Form Handling Techniques

1. Angular Forms & Types

There are mainly two types of forms we need to know in angular,
i) Template-driven Forms
ii) Reactive Forms

Now let's see how to use this forms with example.

2. Template-Driven Forms

This is the type of form that uses ngModel directive to keep track of input using two-way binding.

The form which uses this kind of ngModel mechanism is now called Template-Driven forms.

ngModel and other form related directives are not available by default. So, before we start we need to explicitly import them into our application.

// app.module.ts
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';


import { AppComponent } from './app.component';
import { TemplateDrivenFormsComponent } from './template-driven-forms/template-driven-forms.component';
import { FormsModule } from '@angular/forms'; // <-- import from angular forms


@NgModule({
  declarations: [AppComponent, TemplateDrivenFormsComponent],
  imports: [BrowserModule, FormsModule], // <-- add it here
  providers: [],
  bootstrap: [AppComponent],
})
export class AppModule {}

Enter fullscreen mode Exit fullscreen mode

We've enable the Template Driven Forms by adding FormsModule in to our application.

Our First Template-Driven form

<!-- template-driven-forms.component.html -->
<div>
    <h1>Template-driven Form</h1>
    <form #myForm="ngForm" (ngSubmit)="onSubmit(myForm.value)">
        <div>
            <label>First Name:</label>
            <input type="text" name="firstName [(ngModel)]="user.firstName" required>
        </div>
        <div>
            <label>Last Name:</label>
            <input type="text" name="lastName" [(ngModel)]="user.lastName" required>
        </div>
        <div>
            <label>Password:</label>
            <input type="password" name="password" [(ngModel)]="user.password" required>
        </div>
        <button type="submit" [disabled]="!myForm.valid">Submit</button>
    </form>
</div>
Enter fullscreen mode Exit fullscreen mode
// template-driven-forms.component.ts
import { Component, OnInit } from '@angular/core';

@Component({
  selector: 'app-template-driven-forms',
  templateUrl: './template-driven-forms.component.html',
  styleUrls: ['./template-driven-forms.component.scss'],
})
export class TemplateDrivenFormsComponent implements OnInit {
  user = {
    firstName: 'foo',
    lastName: 'bar',
    password: 'foo@bar',
  };

  constructor() {}

  ngOnInit(): void {}

  onSubmit(user: typeof this.user) {
    console.log(user);
  }
}

Enter fullscreen mode Exit fullscreen mode

ngForms

You can clearly see that we've created a reference for ngForm template as myForm. ngForm can be only be created for HTML <form> element.

This template is responsible for tracking all the of the form, which contains the value of form fields.

Also ngForm keeps track of the validity state of the form, that depends on the validity state of each of it's form field.

ngSubmit

Next to (ngSubmit) this event prevent the submission form to call backend HTTP POST request, like in the case of a plain HTTP form submit.

ngSubmit directive will ensure that plain form submit not to occur instead it calls onSubmit(myForm.value) function with the value of the form that can be accessed by the reference(myForm) we've created

CSS classes

Angular FormsModule provide css classes to apply style to the input based on the state of the input field.

ng-touched or ng-untouched
ng-valid or ng-invalid
ng-pristine or ng-dirty
These are the six classes provided by FormsModule for each form field in the form.

These validity states can be accessed by,

myForm.{nameOfField}.$touched
myForm.{nameOfField}.$valid
myForm.{nameOfField}.$dirty
// validity state of the form that we used to disable the button
myForm.valid
Enter fullscreen mode Exit fullscreen mode

2. Reactive Forms

A reactive form is pretty much like template-driven form. But in order to achieve this we need to add another module called ReactiveFormsModule.

// app.module.ts
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';


import { AppComponent } from './app.component';
import { TemplateDrivenFormsComponent } from './template-driven-forms/template-driven-forms.component';
import { ReactiveFormsModule } from '@angular/forms'; // <-- import from angular forms


@NgModule({
  declarations: [AppComponent, TemplateDrivenFormsComponent],
  imports: [BrowserModule, ReactiveFormsModule], // <-- add it here
  providers: [],
  bootstrap: [AppComponent],
})
export class AppModule {}

Enter fullscreen mode Exit fullscreen mode

Note that we've imported ReactiveFormsModule instead of FormsModule and that enables us to use reactive forms in our application.

Our First Reactive form

<!-- reactive-forms.component.html -->
<div>
    <h1>Reactive Form</h1>
    <form [formGroup]="form" (ngSubmit)="onSubmit()">
        <div>
            <label>First Name:</label>
            <input type="text" formControlName="firstName">
        </div>
        <div>
            <label>Last Name:</label>
            <input type="text" formControlName="firstName">
        </div>
        <div>
            <label>Password:</label>
            <input type="password" formControlName="password">
        </div>
        <button type="submit" [disabled]="!form.valid">Submit</button>
    </form>
</div>

Enter fullscreen mode Exit fullscreen mode

Note that we haven't used any references of form and we've removed the required validation in and also there is no ngModel.
And That's the magic of reactive forms each and every control of the form is in our hands (i.e) we can control every state and value of each form fields in the TS file of this component.

FormGroup

FormGroup is the main thing we need to know about Reactive Forms and it has the control of every form fields that contains.

// reactive-forms.component.ts
import { Component, OnInit } from '@angular/core';
import { FormControl, FormGroup, Validators } from '@angular/forms';

@Component({
  selector: 'app-reactive-forms',
  templateUrl: './reactive-forms.component.html',
  styleUrls: ['./reactive-forms.component.scss'],
})
export class ReactiveFormsComponent implements OnInit {
  form = new FormGroup({
    firstName: new FormControl('', Validators.required),
    lastName: new FormControl('', Validators.required),
    password: new FormControl('', Validators.required),
  });

  constructor() {}

  ngOnInit(): void {}
  onSubmit() {
    console.log('reactive form submitted');
    console.log(this.form);
  }
}

Enter fullscreen mode Exit fullscreen mode

We can see that the form is really just a FormGroup, which keeps track of the global form value and the validity state.

Using this form group we can create simple form like this and also any kind of complex forms and even FormArray

FormGroup contain multiple FormControl and that is the name we've used inside HTML <input> tag as formControlName so that we can control that input tag.

This form have all the control over it's form fields like,

form.valid
form.invalid
form.control["firstName"].valid & .invalid
form.control["firstName"].touched & .untouched
form.control["firstName"].pristine
form.control["firstName"].enabled & .disabled
// or we can also get the fields using
form.get('firstName')
Enter fullscreen mode Exit fullscreen mode

The same thing we've done in template-driven forms.

Also it has many features like,

  1. reset() the form.
  2. setValue('') to any form fields.
  3. patchValue('') to any form fields.
  4. setValue('') to form and it need every value of form field.
  5. patchValue('') to form and it doesn't need every value of form field.
  6. setError('') to any form fields.
  7. we can track each input change using .valueChanges.subscribe((value) => {console.log(value)})

FromControl

It is used to create field inside the (FormGroup)[#group].
It contains the fields default value, disabled or enabled state and validation using Validators

new FormControl({value:"",disabled:false}, [Validators.required])
Enter fullscreen mode Exit fullscreen mode

Validators

It is used to validate the form field in many ways like,

Validators.required
Validators.email
Validators.min
Validators.max
Validators.minLength
Validators.maxLength
Validators.pattern // we can create validation using regEx
Enter fullscreen mode Exit fullscreen mode

These validations errors are accessed using the FormGroup.

this.form.errors // will return all the errors in the form
this.form.controls["field-name"].errors // will return errors of particular field
Enter fullscreen mode Exit fullscreen mode

There are many advanced techniques in Reactive forms like,

  1. Form Builder Api
  2. Form Array
  3. Error Handling Let's see these topics in the next post.

Happy Coding 💻 :).

Top comments (2)

Collapse
 
priyadharshan_senthil_2e1 profile image
Priyadharshan senthil • Edited

Really helpful post. This would be very useful for my current project 😊

Collapse
 
suresh02 profile image
Suresh Hariharan

excellent explanation👏🏽