Form validation in Angular
Mayar Deeb
Posted on May 9, 2022
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.
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.
1- In form.component.ts
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
<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
Congratulations 🥳🥳🥳 that's it.
💖 💪 🙅 🚩
Mayar Deeb
Posted on May 9, 2022
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.
Related
programming Angular Rendering Strategies: Boost Performance and SEO with Expert Techniques
November 20, 2024
angular I take my morning coffee with a hint of Angular: Quick Tip #1 - Shortened import paths in TSConfig
November 15, 2024