Reputation: 39
I followed the short guide on Angular's website for Creating asynchronous validators that can be found at this url.
https://angular.io/guide/form-validation#implementing-a-custom-async-validator
As a result, I have this code:
import { AsyncValidator, AbstractControl, ValidationErrors } from '@angular/forms';
import { Observable, of } from 'rxjs';
import { map, catchError } from 'rxjs/operators';
import { CompanyService } from '../services/company/company.service';
@Injectable({ providedIn: 'root' });
export class UniqueCompanyNameValidator implements AsyncValidator {
constructor(private companyService: CompanyService) { }
validate(
ctrl: AbstractControl
): Promise<ValidationErrors | null> | Observable<ValidationErrors | null> {
return this.companyService.CheckName(ctrl.value).pipe(
map(isTaken => (isTaken ? { uniqueCompanyName: true } : null)),
catchError(() => of(null))
);
}
}
How do I add into my form control to actually use this custom validator? I tried to import the validator into my component and then adding it to the FormControl like the following code shows but got an error.
import { UniqueCompanyNameValidator } from '../../../shared/validators/custom.validators';
'company' : new FormControl(null, { validators: [Validators.required, Validators.minLength(2)], updateOn: 'blur' }, [UniqueCompanyNameValidator]),
The error that I got was the following:
Type 'typeof UniqueCompanyNameValidator' is not assignable to type 'AsyncValidatorFn'.
Type 'typeof UniqueCompanyNameValidator' provides no match for the signature '(control: AbstractControl): Promise<ValidationErrors | null> | Observable<ValidationErrors | null>'.
(alias) class UniqueCompanyNameValidator
import UniqueCompanyNameValidator
Upvotes: 1
Views: 11215
Reputation: 725
I must admit that the guide you linked is misleading for beginners. You could use the given service to create a directive which will inject UniqueAlterEgoValidator.
like so:
@Injectable({ providedIn: 'root' })
export class UniqueAlterEgoValidator implements AsyncValidator {
constructor(private heroesService: HeroesService) {}
validate(
ctrl: AbstractControl
): Promise<ValidationErrors | null> | Observable<ValidationErrors | null> {
return this.heroesService.isAlterEgoTaken(ctrl.value).pipe(
map(isTaken => (isTaken ? { uniqueAlterEgo: true } : null)),
catchError(() => of(null))
);
}
}
@Directive({
selector: '[myValidator]',
providers: [{provide: NG_VALIDATORS, useExisting: MyValidatorDirective , multi: true}]
})
export class MyValidatorDirective implements AsyncValidator {
constructor(private validator: UniqueAlterEgoValidator) {}
validate(control: AbstractControl): Promise<ValidationErrors | null> | Observable<ValidationErrors | null> {
return this.validator.validate(control);
}
}
With a template driven form: create a Directive that implements AsyncValidator.
With a reactive form: create a function that returns a AsyncValidatorFn.
See the angular guide about custom validators.
Upvotes: 0
Reputation: 32550
It must be a function/validator instance, not injectable token (class/type). In your example to make it work you have to pass validate
function, not Service
token. Assuming that it is this.companyService
it would be like
new FormControl(null, { validators: [Validators.required, Validators.minLength(2)], updateOn: 'blur' }, [this.comapnyService.validate.bind(this.companyService)]),
or maybe (I never use that variant)
new FormControl(null, { validators: [Validators.required, Validators.minLength(2)], updateOn: 'blur' }, [this.comapnyService]),
I would create a ValidatorFn factory method that takes required service as and argument and go from there.
export class MyValidators{
static uniqueCompanyName(companyService:SompanyService){
return (ctrl:AbstractControl)=>{
return companyService.doCheCkeck(ctrl.value).pipe(
map(isTaken => (isTaken ? { uniqueCompanyName: true } : null))
);
}
}
}
and then
'company' : new FormControl(null, { validators: [Validators.required, Validators.minLength(2)], updateOn: 'blur' }, [MyValidators.uniqueCompanyName(companyServiceInstance)]),
Upvotes: 0