I'm trying to use AsyncValidation in Angular with a service method that has async/await, to test if a userName exists. I can't figure out how to convert the return signature in the service (user.service.ts)
Promise<boolean>
e.g.
async isUserNameAvailable(userName: string): Promise<boolean> {
}
to
Promise<ValidationErrors | null> | Observable<ValidationErrors | null>
e.g.
validate(control: AbstractControl): Promise<ValidationErrors | null> | Observable<ValidationErrors | null> {
}
in the Validator/Directive.
user.service.ts:
async isUserNameAvailable(userName: string): Promise<boolean> {
var query = this.db.collection("users").where("name", "==", userName);
try {
const documentSnapshot = await query.get();
if (documentSnapshot.empty) {
return true;
} else {
return false;
}
} catch (error) {
console.log('Error getting documents', error);
}
}
existing-username-validator.directive.ts
import { Directive } from '@angular/core';
import { UserService } from './user.service';
import { AbstractControl, ValidationErrors, NG_ASYNC_VALIDATORS, AsyncValidatorFn, AsyncValidator } from '@angular/forms';
import { Observable, timer } from 'rxjs';
import { map, filter, switchMap } from 'rxjs/operators';
export function existingUsernameValidator(userService: UserService): AsyncValidatorFn {
return (control: AbstractControl): Promise<ValidationErrors | null> | Observable<ValidationErrors | null> => {
let debounceTime = 500; //milliseconds
return Observable.timer(debounceTime).switchMap(()=> { //ERROR BECAUSE OF TIMER
return userService.isUserNameAvailable(control.value).map( //ERROR BECAUSE OF map
users => {
return (users && users.length > 0) ? {"usernameExists": true} : null;
}
);
});
};
}
@Directive({
selector: '[appExistingUsernameValidator]',
providers: [{provide: NG_ASYNC_VALIDATORS, useExisting: ExistingUsernameValidatorDirective, multi: true}]
})
export class ExistingUsernameValidatorDirective implements AsyncValidator {
constructor(private userService: UserService) { }
validate(control: AbstractControl): Promise<ValidationErrors | null> | Observable<ValidationErrors | null> {
return existingUsernameValidator(this.userService)(control);
}
}
user.component.ts:
name: new FormControl('', {
validators: [Validators.required],
asyncValidators: [existingUsernameValidator(this.userService)]
}),
Stackblitz: https://stackblitz.com/edit/angular-ivy-cd866c?file=src%2Fapp%2Fuser.service.ts
Does anyone know how to achieve this using Reactive Forms?