I have a form group with a control which has a validator attached to it. The controls update on change. Everything works perfectly, but for better user experience it would be great if validators are executed on change after X seconds the user stopped typing.
ngOnInit(): void {
this.signUpForm = new FormGroup(
{
userName: new FormControl(null, [
UsernameValidator.validate,
]),
},
{
updateOn: 'change',
}
);
}
The validator:
export class UsernameValidator {
static validate(control: AbstractControl): ValidationErrors | null {
const regex = /^[a-zA-Z0-9_.]+$/;
if (control.value) {
if (!regex.test(control.value)) {
return { [ErrorTypes.invalidUserName]: true };
}
if (control.value.length < 3) {
return { [ErrorTypes.userNameTooShort]: true };
}
if (control.value.length > 30) {
return { [ErrorTypes.userNameTooLong]: true };
}
}
return null;
}
}
I know these is synchronous validation, but is it even possible to achieve the delay?
>Solution :
It is possible to do it if you make your code async
along with DebounceTime
import { FormGroup, FormControl, AbstractControl, ValidationErrors } from '@angular/forms';
import { Observable, of } from 'rxjs';
import { debounceTime, map, switchMap } from 'rxjs/operators';
import { YourAsyncValidationService } from 'path-to-your-async-validation-service';
export class UsernameValidator {
static validateAsync(service: YourAsyncValidationService): (control: AbstractControl) => Observable<ValidationErrors | null> {
return (control: AbstractControl): Observable<ValidationErrors | null> => {
const regex = /^[a-zA-Z0-9_.]+$/;
// If the control is empty, return observable of null (no error)
if (!control.value) {
return of(null);
}
// Add a delay of 500 milliseconds using debounceTime
return of(control.value).pipe(
debounceTime(500),
switchMap(() => service.validateUsernameAsync(control.value)),
map(result => {
if (!regex.test(control.value)) {
return { [ErrorTypes.invalidUserName]: true };
}
if (control.value.length < 3) {
return { [ErrorTypes.userNameTooShort]: true };
}
if (control.value.length > 30) {
return { [ErrorTypes.userNameTooLong]: true };
}
return result ? { [ErrorTypes.asyncValidationFailed]: true } : null;
})
);
};
}
}
export class YourComponent {
signUpForm: FormGroup;
constructor(private yourAsyncValidationService: YourAsyncValidationService) {}
ngOnInit(): void {
this.signUpForm = new FormGroup(
{
userName: new FormControl(null, {
asyncValidators: [UsernameValidator.validateAsync(this.yourAsyncValidationService)],
}),
},
{
updateOn: 'change',
}
);
}
}