Follow

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use
Contact

Delay Reactive form validation Angular

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?

MEDevel.com: Open-source for Healthcare and Education

Collecting and validating open-source software for healthcare, education, enterprise, development, medical imaging, medical records, and digital pathology.

Visit Medevel

>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',
      }
    );
  }
}

Add a comment

Leave a Reply

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use

Discover more from Dev solutions

Subscribe now to keep reading and get access to the full archive.

Continue reading