Am trying to implement custom validators. The non-async one (cannotContainSpaces
) works just fine. The async one (shouldBeUnique
), which, yes, is trivial at the moment, should be returning promise as I understand, which the Validator object should resolve. It doesn't. The errors collection on the formControl username
shows this in the console:
{__zone_symbol__state: null, __zone_symbol__value: Array(0)}
Form component:
import { CustomValidators } from './custom.validators';
import { Component, Input } from '@angular/core';
import { FormGroup, FormControl, Validators } from '@angular/forms';
@Component({
selector: 'signup-form',
templateUrl: './signup-form.component.html',
styleUrls: ['./signup-form.component.css']
})
export class SignupFormComponent {
form = new FormGroup({
username: new FormControl('', [
CustomValidators.cannotContainSpaces,
CustomValidators.shouldBeUnique
// Validators.email,
]),
password: new FormControl('', Validators.required)
})
get username() {
return this.form.get('username');
}
keyPressed(){
console.log(this.username.errors)
}
}
Custom validator method:
import { AbstractControl, ValidationErrors } from "@angular/forms";
export class CustomValidators {
static cannotContainSpaces(control: AbstractControl) : ValidationErrors | null {
if ((<string>control.value).indexOf(' ') >= 0)
return { cannotContainSpaces: true};
return null;
}
static shouldBeUnique(control: AbstractControl) : Promise<ValidationErrors | null> {
return new Promise((resolve, reject) => {
setTimeout(function() {
if (control.value === 'treve')
resolve({shouldBeUnique: true});
else resolve(null);
}, 2000);
});
}
}
Relevant HTML:
<form [formGroup]="form">
<div class="form-group">
<label for="username">Username</label>
<input
(keyup) = "keyPressed()" (blur) = "keyPressed()"
formControlName="username"
id="username"
type="text"
class="form-control">
<div *ngIf="username.touched && username.invalid" class="alert alert-danger">
<div *ngIf="username.errors.cannotContainSpaces">Username must not contain spaces</div>
<div *ngIf="username.errors.shouldBeUnique">Sorry, that username has been taken</div>
</div>
</div>
Angular provides AsyncValidatorFn interface to create custom async validator function that will be used by FormControl in reactive form. Find the structure of AsyncValidatorFn interface from Angular doc. AsyncValidatorFn has a method declaration that has argument as AbstractControl and it will contain latest value of the form control.
Async validator directive is used with ngModel in template-driven from. Async validator is used to validate data against the data located at remote server. To avoid frequent hit to server we should also use synchronous (sync) validators such as required, email, minlength and maxlength etc.
The NG_ASYNC_VALIDATORS is used as provider with multi: true in our async validator directive. Async validator directive is used in HTML template in reactive form or with ngModel in template-driven from. Async validator is used to validate data against the data located at remote server.
certain validators can need to perform an asynchronous operation, like calling a backend or a promise-based library. This can be implemented by having the validator return either a promise or an observable For validation to work properly, it's usually necessary to fine-tune exactly when the validators of a field are triggered.
Async validators are to be set as the third argument:
username: ['', [sync validators here], [async validators here]]
so change the the following:
username: new FormControl('',
[
CustomValidators.cannotContainSpaces,
CustomValidators.shouldBeUnique
]),
to:
username: new FormControl('',
[CustomValidators.cannotContainSpaces],
[CustomValidators.shouldBeUnique]
),
DEMO: http://plnkr.co/edit/OceHbSl3atPHdcvNRQDs?p=preview
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With