Skip to content

Instantly share code, notes, and snippets.

@Humberd
Last active October 31, 2019 19:00
Show Gist options
  • Star 8 You must be signed in to star a gist
  • Fork 3 You must be signed in to fork a gist
  • Save Humberd/b033c2c4df3ac0eae9b466038a433635 to your computer and use it in GitHub Desktop.
Save Humberd/b033c2c4df3ac0eae9b466038a433635 to your computer and use it in GitHub Desktop.
Async Validator Wrapper for Angular 2 Reactive Forms.This wrapper lets you debounce your asyncValidator instead of on every keystroke
import { AbstractControl } from "@angular/forms";
import { Observable } from "rxjs/Observable";
import { Subject } from "rxjs/Subject";
export class QstAsyncValidatorWrapper {
/*
* Angular async validators are triggered on every key stroke.
* This function debounces triggering an async validator.
*
* How to use?
* Let's say you have a function which asynchronously validates an email:
*
* function asyncEmailValidator (c: AbstractControl): Observable<any> {
* return checkInRemote();
* }
*
* In your Reactive Form Builder instead of doing this:
*
* email: new FormControl("", [
* Validators.required
* ], [
* asyncEmailValidator
* ])
*
* Do this to debounce every 800ms:
*
* email: new FormControl("", [
* Validators.required
* ], [
* QstAsyncValidatorWrapper.debounce(asyncEmailValidator, 800)
* ])
*
* */
public static debounce(asyncValidator: (c: AbstractControl) => Observable<any>,
time: number = 500): (c: AbstractControl) => Observable<any> {
/*Starting a debouncing observable*/
const subject: Subject<AbstractControl> = new Subject();
const obs: Observable<any> = subject
.debounceTime(time)
.switchMap(abstractControl => asyncValidator(abstractControl))
.share();
/*Need to have at least 1 active subscriber, because otherwise
* the first `subject.next(c)` event won't be registered*/
obs.subscribe();
return (c: AbstractControl) => {
/*Every time this function is invoked by Angular I must inform a subject about it*/
subject.next(c);
/*Need to take only one for every function invocation,
* because the subscription must complete.
* Otherwise Angular form state would be "PENDING"*/
return obs.first();
};
}
}
@attl8d
Copy link

attl8d commented Jul 6, 2017

Works, beautifully! Thanks!

@alexeyuzlov
Copy link

alexeyuzlov commented Jul 31, 2017

type of first argument is AsyncValidatorFn instead of (c: AbstractControl) => Observable.
It's not compatible with angular (4+) abstract control API. Check setAsyncValidators() method

@mattiasnordqvist
Copy link

mattiasnordqvist commented Feb 28, 2018

this should be a npm package! 👍 :)

Edit: I created a angular5-compatible version, and published it to npm as an exercise for myself (never published any npm packages before). Hope you don't mind.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment