Learn how to inject service into Validator in Angular. The validator may depend on some external service to do its validation. For Example, it may need to fetch data from the back end server to validate the value.
This is a continuation of our following tutorials
Table of Contents
Custom Validator
Here is our greater than custom validator gte
from the previous tutorial. The gte
is a function that returns ValidatorFn function.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | import { AbstractControl, ValidationErrors, ValidatorFn } from '@angular/forms' export function gte(val: number): ValidatorFn { return (control: AbstractControl): ValidationErrors | null => { let v: number = +control.value; if (isNaN(v)) { return { 'gte': true, 'requiredValue': val } } if (v <= +val) { return { 'gte': true, 'requiredValue': val } } return null; } } |
We pass the parameter to the validator in the component class as shown below.
1 2 3 4 5 | myForm = new FormGroup({ numVal: new FormControl('', [gte(10)]), }) |
Validator Service
Let us now see how we can inject service into the above validator. First, move the logic from the gte
validator to a separate service.
Create a new service gte.service.ts
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | import { Injectable } from '@angular/core'; @Injectable({ providedIn: 'root', }) export class gteService { gte(num:any, requiredValue:Number) : Boolean { if (isNaN(num)) { return false; } if (num <= +requiredValue) { return false; } return true; } } |
The gteService
is very simple.
The gte
method takes val
and requiredValue
as the parameter. It checks if the val
is a number and is greater than requiredValue
. If yes returns true
else returns false
.
Injecting Service
There are two ways you can inject service into the validator. One is to create a wrapper service. The other option is to inject service directly into the validator.
Wrapper Service
Open the gte.validator.ts
. Create the gteValidatorService
class. In the constructor inject the gteService
. Copy the validator functiongte
into the class and use the gteService
as shown below
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | import { AbstractControl, ValidationErrors, ValidatorFn, Validator, NG_VALIDATORS, FormControl } from '@angular/forms' import { gteService } from './gte.service'; import { Directive, OnInit, forwardRef, Input, Injectable } from '@angular/core'; @Injectable({ providedIn: 'root', }) export class gteValidatorService { constructor(private gteService: gteService) { } gte(val: number): ValidatorFn { return (control: AbstractControl): ValidationErrors | null => { let v: number = +control.value; if (!this.gteService.gte(v,val)) { return { 'gte': true, 'requiredValue': val } } return null; } } } |
Inject the gteValidatorService
in the component class and use the validator as shown below.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 | import { Component } from '@angular/core'; import { FormGroup, FormControl, AbstractControl, ValidationErrors, ValidatorFn } from '@angular/forms' import { gteValidatorService } from './gte.validator'; @Component({ selector: 'app-root', templateUrl: './app.component.html', styleUrls: ['./app.component.css'] }) export class AppComponent { constructor(private gteValidator:gteValidatorService) { } myForm = new FormGroup({ numVal: new FormControl('', [this.gteValidator.gte(10)]), }) get numVal() { return this.myForm.get('numVal'); } onSubmit() { console.log(this.myForm.value); } } |
Inject Service directly into the Validator
Another option is to directly inject the service into the Validator as shown below.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | import { AbstractControl, ValidationErrors, ValidatorFn, Validator, NG_VALIDATORS, FormControl } from '@angular/forms' import { gteService } from './gte.service'; import { Directive, OnInit, forwardRef, Input, Injector } from '@angular/core'; export function gte(val: number): ValidatorFn { return (control: AbstractControl): ValidationErrors | null => { let injector = Injector.create([ { provide: gteService, useClass:gteService,deps: []}]) let service = injector.get(gteService); let v: number = +control.value; if (!service.gte(v, val)) { return { 'gte': true, 'requiredValue': val } } return null; } } |
We use the Injector
to inject the instance of the service.
1 2 3 4 | let injector = Injector.create([ { provide: gteService, useClass:gteService,deps: []}]) let service = injector.get(gteService); |
Summary
In this article, we learned how to inject the service into the validator function. One option is to create a Validator service. Copy the validator function into the Validator service. Another option is to inject the service directly into the validator function using the inject method.
Complete List of Angular Forms Tutorial
- Angular Forms Tutorial: Fundamental & Concepts
- Template Driven Forms in Angular
- Set Value in Template Driven forms in Angular
- Reactive Forms in Angular
- FormBuilder in Reactive Forms
- SetValue & PatchValue in Angular
- StatusChanges in Angular Forms
- ValueChanges in Angular Forms
- FormControl
- FormGroup
- FormArray Example
- Build Dynamic or Nested Forms using FormArray
- Validations in Reactive Forms in Angular
- Custom Validator in Reactive Forms
- Passing Parameter to Custom Validator in Reactive Forms
- Inject Service into Custom Validator
- Validation in Template Driven Forms
- Custom Validator in Template Driven Forms
How to access errors in HTML?