Form validation in Angular
Tags: angular, typescript
Published: 2022-05-09 11:49:07

Form validation is one of the most important things to learn in Angular.

Today, I will explain the best way I have found to write validation in Angular; the goal here is to save time and have clean readable code.

## 1-Add `FormsModule` and `ReactiveFormsModule` to your module.

import `FormsModule` and `ReactiveFormsModule` and add them to the imports array.


```typescript 
import {  FormsModule, ReactiveFormsModule } from '@angular/forms';

@NgModule({
  imports: [
///.... the rest or your modules
    FormsModule,
     ReactiveFormsModule,
    
  ],
  bootstrap: [AppComponent]
})
export class AppModule { }

```

## 2-Write your code
Our goal is to create the following form,so I'll be using `Tailwind` here.

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/1lvqa2qycl1tp4wlo1d6.png)
 

1- In `form.component.ts`

```typescript
import { Component, OnInit } from '@angular/core';
import { FormControl, FormGroup, Validators,FormBuilder} from '@angular/forms';

@Component({
  selector: 'app-form',
  templateUrl: './form.component.html'
})
export class FormComponent implements OnInit {
  constructor() { }

// you can add initial values if you are updating something 
  formgroup = new FormBuilder().group({
    email: new FormControl('initial value here', [Validators.minLength(2),Validators.required,Validators.email]),
    password: new FormControl('', [Validators.required]),
    checkPassword: new FormControl('', [Validators.required]),
  },  { validator: this.match('password','checkPassword') }
  );

/* 
 This function will return a Boolean value. 
If you want to check whether the input 
 has any error, pass the input name as first parameter.
And
 if it has a specific error, pass the input name as first 
 parameter and the error name as the second parameter 
*/
  hasError(InputName: string,error?:'maxlength'|'minlength'|'required'|'email'|'confirmed'|'max'|'min'|'pattern'):boolean {
if(error){
  return this.formgroup.get(InputName)?.hasError(error) &&
  (this.formgroup.get(InputName)?.dirty || this.formgroup.get(InputName)?.touched)?true:false
}else{
  return this.formgroup.get(InputName)?.invalid &&
  (this.formgroup.get(InputName)?.dirty || this.formgroup.get(InputName)?.touched)?true:false
}
  }

// usually I Put this function in separate ts file
// I create helpers folder in src/helpers then create files for 
//the functions that I use too much in my project

  match(controlName: string, matchingControlName: string) {
    return (formGroup: FormGroup) => {
      const control = formGroup.controls[controlName];
      const matchingControl = formGroup.controls[matchingControlName];
      if (matchingControl.errors && !matchingControl.errors['confirmed']) {
        return;
      }
      if (control.value !== matchingControl.value) {
        matchingControl.setErrors({ confirmed: true });
      } else {
        matchingControl.setErrors(null);
      }
    };
  }

// this function won't trigger unless the form is valid
// we'll see how in the form.component.html
  onSubmit(input: any): void {
    console.log(input)
  }

  ngOnInit(): void {

  }


}

```

2- In `form.component.html`

```html
    <form
      [formGroup]="formgroup"
      (ngSubmit)="onSubmit(formgroup.value)"
      class="w-6/12 mx-auto"
    >
      <p class="mb-4"></p>
      <div class="mb-4">
<!--Add formControlName for each input -->
<!-- If you want to apply different styles on errors all 
 what you need to do is to use the hasError() function -->
        <input
          type="text"
          formControlName="email"
          class="form-control block w-full px-3 py-1.5 text-base font-normal 
          text-gray-700 bg-white bg-clip-padding border border-solid
           rounded m-0 focus:text-gray-700 focus:bg-white focus:outline-none"
          [ngClass]="
            hasError('email')
              ? 'border-red-300 focus:border-red-600'
              : 'border-gray-300 focus:border-blue-600'
          "
          placeholder="email"
        />
<!-- hasError() function takes two parameters the first one is 
formControlName and the second is error type.
you need to know if you don't add the second parameter, it'll
 return if the input has any errors or not. -->
        <div
          class="m-1 text-red-500 capitalize"
          *ngIf="hasError('email', 'required')"
        >
          Please enter email
        </div>
        <div
          class="m-1 text-red-500 capitalize"
          *ngIf="hasError('email', 'email')"
        >
          Please enter valid email
        </div>
      </div>

      <div class="mb-4">
        <input
          type="text"
          formControlName="password"
          class="form-control block w-full px-3 py-1.5 text-base font-normal text-gray-700
           bg-white bg-clip-padding border border-solid rounded m-0 focus:text-gray-700 
           focus:bg-white focus:outline-none"
          [ngClass]="
            hasError('password')
              ? 'border-red-300 focus:border-red-600'
              : 'border-gray-300 focus:border-blue-600'
          "
          placeholder="password"
        />
        <div
          class="m-1 text-red-500 capitalize"
          *ngIf="hasError('password', 'required')"
        >
          Please enter password
        </div>
      </div>

      <div class="mb-4">
        <input
          type="text"
          formControlName="checkPassword"
          class="form-control block w-full px-3 py-1.5 text-base font-normal 
          text-gray-700 bg-white bg-clip-padding border border-solid 
          rounded m-0 focus:text-gray-700 focus:bg-white focus:outline-none"
          [ngClass]="
            hasError('checkPassword')
              ? 'border-red-300 focus:border-red-600'
              : 'border-gray-300 focus:border-blue-600'
          "
          placeholder="checkPassword"
        />
        <div
          class="m-1 text-red-500 capitalize"
          *ngIf="hasError('checkPassword', 'required')"
        >
          Please enter checkPassword
        </div>
        <div
          class="m-1 text-red-500 capitalize"
          *ngIf="hasError('checkPassword', 'confirmed')"
        >
          The two passwords do not match
        </div>
      </div>

      <div class="text-center pt-1 mb-12 pb-1">
<!-- here we should add [disabled]="formgroup.invalid" 
to make sure that the user won't be able to submit unless 
the form is valid, also it's useful in testing making it easier -->
        <button
          [disabled]="formgroup.invalid"
          class="main-color inline-block px-6 py-2.5 text-white font-medium text-xs leading-tight uppercase
           rounded shadow-md hover:bg-blue-700 hover:shadow-lg focus:shadow-lg focus:outline-none focus:ring-0
            active:shadow-lg transition duration-150 ease-in-out w-full mb-3"
          type="submit"
        >
          Submit
        </button>
      </div>
    </form>
```
you're page should look like this
![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/s1tq21157re5kfbjoo53.png)

Congratulations 🥳🥳🥳 that's it.