Angular Custom Validator with Parameter

By Arvind Rai, 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);
  }
} 
a. In the above code, we have a 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)]]
}); 
HTML code:
<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> 
TS code:
mobStdCode = 91; 

Complete Example

In our demo application, we have two custom validator as personNamePrefix 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);
  }
} 
person-reactive-form.component.ts
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();
    }
  }
} 
person-reactive-form.component.html
<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> 
person-template-driven-form.component.ts
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();
    }
  }
} 
person-template-driven-form.component.html
<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> 
person-service.ts
import { Injectable } from '@angular/core';

@Injectable({
  providedIn: 'root'
})
export class PersonService {
  createPerson(person: any) {
    console.log(JSON.stringify(person));
  }
} 
Find the print-screen of the output.
Angular Custom Validator with Parameter

References

Download Source Code

POSTED BY
ARVIND RAI
ARVIND RAI







©2024 concretepage.com | Privacy Policy | Contact Us