Angular Custom Validator with Parameter
January 18, 2024
On this page, we will learn to create custom validator with parameter. To create custom validation, use following Angular API.
ValidatorFn : A function that validates a control synchronously. It receives a control and returns a map of validation errors, if any.
Validator : Interface implemented by custom validator directive to perform synchronous validator. It has
validate()
method that receives a control and returns a map of validations errors, if any.
Custom validator is created using
ValidatorFn
for reactive form. For FormControl
validation, we assign a validator function. To create validator with parameter, create parametrized validator function.
Validator
interface is used to create custom validator directive that can be used in template-driven form. To get parameter, use @Input()
decorator.
Steps to Create and Use
Find the steps to create and use custom validator with parameter.Step-1: Find the custom validator code used in our demo application.
stdcode-validator.directive.ts
import { Directive, Input } from '@angular/core'; import { NG_VALIDATORS, ValidationErrors, Validator, FormControl } from '@angular/forms'; import { ValidatorFn, AbstractControl } from '@angular/forms'; export function stdCodeValidator(stdCode: number): ValidatorFn { return (control: AbstractControl): ValidationErrors | null => { const v = control.value; return (v !== null && v !== '' && !v.startsWith(stdCode)) ? { "stdCode": true } : null; }; } @Directive({ selector: '[stdCode]', standalone: true, providers: [{ provide: NG_VALIDATORS, useExisting: STDCodeDirective, multi: true }] }) export class STDCodeDirective implements Validator { @Input() stdCode!: number; validate(control: FormControl): ValidationErrors | null { return stdCodeValidator(this.stdCode)(control); } }
stdCodeValidator()
function to validate a FormControl
that accepts a parameter. The user input is matched against the value passed to this method. To display validation error message, use stdCode
.
b.
STDCodeDirective
can be used in template-driven form. It accepts parameter using @Input()
decorator.
Step-2: Find the code to use above custom validator with parameter in reactive form.
TS code:
mobStdCode = 91; personForm = this.formBuilder.group({ mobNum: ['', [stdCodeValidator(this.mobStdCode)]] });
<input formControlName="mobNum"> <div *ngIf="personForm.get('mobNum')?.hasError('stdCode')" ngClass="error"> Start with STD Code {{mobStdCode}} </div>
Step-3: In template-driven form to pass the value for validation parameter, use property binding with
stdCode
.
HTML code:
<input name="mobNum" ngModel [stdCode]="mobStdCode" #mobNum="ngModel"> <div *ngIf="mobNum.errors?.['stdCode']" ngClass="error"> Start with STD Code {{mobStdCode}} </div>
mobStdCode = 91;
Complete Example
In our demo application, we have two custom validator aspersonNamePrefix
and stdCode
. Both validators accept parameter. personNamePrefix
matches the specified prefix for a name entered by user. stdCode
matches the specified STD code with mobile number entered by user. You can get the validator code for stdCode
above in the article. Here I will provide other files.
personname-validator.directive.ts
import { Directive, Input } from '@angular/core'; import { NG_VALIDATORS, Validator, FormControl } from '@angular/forms'; import { ValidatorFn, ValidationErrors, AbstractControl } from '@angular/forms'; export function personNameValidator(personNamePrefix: string): ValidatorFn { return (control: AbstractControl): ValidationErrors | null => { const v = control.value; return (v !== null && v !== '' && !v.startsWith(personNamePrefix)) ? { "personNamePrefix": true } : null; }; } @Directive({ selector: '[personNamePrefix]', standalone: true, providers: [{ provide: NG_VALIDATORS, useExisting: PersonNameValidatorDirective, multi: true }] }) export class PersonNameValidatorDirective implements Validator { @Input() personNamePrefix!: string; validate(control: FormControl): ValidationErrors | null { return personNameValidator(this.personNamePrefix)(control); } }
import { Component } from '@angular/core'; import { FormBuilder, Validators, ReactiveFormsModule } from '@angular/forms'; import { PersonService } from './person-service'; import { personNameValidator } from './validators/personname-validator.directive'; import { CommonModule } from '@angular/common'; import { stdCodeValidator } from './validators/stdcode-validator.directive'; @Component({ selector: 'app-reactive', standalone: true, imports: [CommonModule, ReactiveFormsModule], templateUrl: './person-reactive-form.component.html' }) export class PersonReactiveFormComponent { pnamePrefix = 'Mr. '; mobStdCode = 91; constructor(private formBuilder: FormBuilder, private personService: PersonService) { } personForm = this.formBuilder.group({ pname: ['', [Validators.required, personNameValidator(this.pnamePrefix)]], mobNum: ['', [Validators.required, stdCodeValidator(this.mobStdCode)]] }); onFormSubmit() { if (this.personForm.valid) { this.personService.createPerson(this.personForm.value); this.personForm.reset(); } } }
<h3>Person Reactive Form</h3> <form [formGroup]="personForm" (ngSubmit)="onFormSubmit()"> <table> <tr> <td>Person Name: </td> <td> <input formControlName="pname"> <div *ngIf="personForm.get('pname')?.hasError('required')" ngClass="error"> Enter person name. </div> <div *ngIf="personForm.get('pname')?.hasError('personNamePrefix')" ngClass="error"> Start person name with {{pnamePrefix}} </div> </td> </tr> <tr> <td>Mobile Number: </td> <td> <input formControlName="mobNum"> <div *ngIf="personForm.get('mobNum')?.hasError('required')" ngClass="error"> Enter mobile number. </div> <div *ngIf="personForm.get('mobNum')?.hasError('stdCode')" ngClass="error"> Start with STD Code {{mobStdCode}} </div> </td> </tr> <tr> <td colspan="2"> <button>Submit</button> </td> </tr> </table> </form>
import { Component } from '@angular/core'; import { FormsModule, NgForm } from '@angular/forms'; import { PersonService } from './person-service'; import { CommonModule } from '@angular/common'; import { PersonNameValidatorDirective } from './validators/personname-validator.directive'; import { STDCodeDirective } from './validators/stdcode-validator.directive'; @Component({ selector: 'app-template', standalone: true, imports: [CommonModule, FormsModule, STDCodeDirective, PersonNameValidatorDirective], templateUrl: './person-template-driven-form.component.html' }) export class PersonTemplateDrivenFormComponent { pnamePrefix = 'Mr. '; mobStdCode = 91; constructor(private personService: PersonService) { } onFormSubmit(form: NgForm) { if (form.valid) { this.personService.createPerson(form.value); form.resetForm(); } } }
<h3>Person Template-driven Form</h3> <form #personForm="ngForm" (ngSubmit)="onFormSubmit(personForm)"> <table> <tr> <td>Person Name:</td> <td> <input name="pname" ngModel required [personNamePrefix]="pnamePrefix" #pname="ngModel"> <div *ngIf="pname.errors?.['required']" ngClass="error"> Enter person name. </div> <div *ngIf="pname.errors?.['personNamePrefix']" ngClass="error"> Start person name with {{pnamePrefix}} </div> </td> </tr> <tr> <td>Mobile Number:</td> <td> <input name="mobNum" ngModel required [stdCode]="mobStdCode" #mobNum="ngModel"> <div *ngIf="mobNum.errors?.['required']" ngClass="error"> Enter mobile number. </div> <div *ngIf="mobNum.errors?.['stdCode']" ngClass="error"> Start with STD Code {{mobStdCode}} </div> </td> </tr> <tr> <td colspan="2"> <button>Submit</button> </td> </tr> </table> </form>
import { Injectable } from '@angular/core'; @Injectable({ providedIn: 'root' }) export class PersonService { createPerson(person: any) { console.log(JSON.stringify(person)); } }