Reputation: 2323
I sort of understand that the tools involved in this would be pipe
, timer
, switchMap
, debounceTime
, distinctUntilChanged
, and possibly more, but I cannot figure out how to make them work with combineLatest
.
The following validator gets a comma-separated list of emails, and performs an API call for each of them to check if that email exists in the customers database.
import {AbstractControl, AsyncValidatorFn, ValidationErrors, ValidatorFn} from "@angular/forms";
import {combineLatest} from "rxjs";
import {Observable} from "rxjs/internal/Observable";
import {UtilityApiService} from "../../services/api/utility-api.service";
/**
* The email address must exist in the database and belong to a customer.
* It works both with a single email or a comma separate list of emails. In case of a list, it will only validate
* as a success if ALL emails belong to a customer.
* @returns {ValidatorFn}
* @constructor
*/
export class CustomerEmailCsvValidator {
public static createValidator(internalApiService: UtilityApiService): AsyncValidatorFn {
return (control: AbstractControl): Promise<ValidationErrors | null> | Observable<ValidationErrors | null> => {
return new Observable((observer) => {
if (!control.value) {
observer.next(null);
return;
}
const emails: string[] = control.value.split(",").map(email => email.trim());
const observables: Observable<boolean>[] = emails.map(email => {
return internalApiService.isCustomerEmail(email);
});
combineLatest(observables).subscribe((responses: boolean[]) => {
if (responses.every(value => value)) {
// All emails exist and belong to customers. Therefore no error.
observer.next(null);
} else {
// One or more emails do not exist in the database, or do not belong to a customer.
// This is an error for this validator.
observer.next({customerEmail: true});
}
observer.complete();
});
});
};
}
}
How can I debounce so that it doesn't run the API calls more than once in 750 ms?
Upvotes: 2
Views: 2562
Reputation: 2323
This is what ended up working for me:
import {AbstractControl, AsyncValidatorFn, ValidationErrors, ValidatorFn} from "@angular/forms";
import {combineLatest, of, timer} from "rxjs";
import {Observable} from "rxjs/internal/Observable";
import {map, switchMap} from "rxjs/operators";
import {UtilityApiService} from "../../services/api/utility-api.service";
/**
* The email address must exist in the database and belong to a customer.
* It works both with a single email or a comma separated list of emails. In case of a list, it will only validate
* as a success if ALL emails belong to a customer.
* @returns {ValidatorFn}
* @constructor
*/
export class CustomerEmailCsvValidator {
public static createValidator(internalApiService: UtilityApiService): AsyncValidatorFn {
return (control: AbstractControl): Promise<ValidationErrors | null> | Observable<ValidationErrors | null> => {
return timer(750).pipe(switchMap(() => {
if (!control.value) {
return null;
}
const emails: string[] = control.value.split(",").map(email => email.trim());
const observables: Observable<boolean>[] = emails.map(email => {
return internalApiService.isCustomerEmail(email);
});
return combineLatest(observables).pipe(map((responses: boolean[]) => {
if (responses.every(value => value)) {
// All emails exist and belong to customers. Therefore no error.
return null;
} else {
// One or more emails do not exist in the database, or do not belong to a customer.
// This is an error for this validator.
return {customerEmail: true};
}
}));
}));
};
}
}
Upvotes: 1