Gabb1995
Gabb1995

Reputation: 1037

Angular 7: Custom Async Validator

I'm trying to create a custom async validator for my registration form where it checks if an email already exists. The backend returns 404 if the email does not exist and 200 if it does exist (can't change this old code).

I have found a couple tutorials but didn't find one using the latest rxjs library. I created this Validation class:

export class UniqueEmailValidator {
    static createValidator(httpClient: HttpClient, degree: number, wlId: number) {
        return (control: AbstractControl) => {
            return httpClient.get(`${url}?degree=${degree}&email=${control.value}&wl_id=${wlId}`)
                .pipe(
                    map(
                        (response: Response) => {
                            return response.status === 404 ? null : { emailNotUnique: true };
                        }
                    )
                );
        };
    }
}

and in my ts file creating the form I'm using this

this.registerForm = this.fb.group({
            email: ['', [Validators.required, Validators.email], UniqueEmailValidator.createValidator(
                this.httpClient, this.wlService.wl.degree, this.wlService.wl.id)],

The xhr call is being done and returned correctly but the form control of email is staying as pending. Any ideas on what I did wrong?

Upvotes: 3

Views: 2475

Answers (1)

Gabb1995
Gabb1995

Reputation: 1037

Figured it out after a while and more research.

Validation class:

@Injectable()
export class UniqueEmailValidator {
    constructor(private http: HttpClient) {}

    searchEmail(email: string, degree: number, wlId: number) {
        return timer(1000)
            .pipe(
                switchMap(() => {
                    // Check if email is unique
                    return this.http.get<any>(`${url}?degree=${degree}&email=${email}&wl_id=${wlId}`);
                })
            );
    }

    createValidator(degree: number, wlId: number): AsyncValidatorFn {
        return (control: AbstractControl): Observable<{ [key: string]: any } | null> => {
            return this.searchEmail(control.value, degree, wlId)
                .pipe(
                    map(
                        (response: Response) => {
                            return null;
                        },
                    ),
                    catchError(
                        (err: any) => {
                            return err.status === 404 ? of(null) : of({ emailNotUnique: true });
                        },
                    ),
                );
        };
    }
}

Not sure if timer can be changed but I found it in an article and it works fine. Would love some confirmation on that.

Basically I'm doing a catchError since the response from the backend returns 404 and returning an observable again from the catchError.

Then in the form creation I'm doing:

        this.registerForm = this.fb.group({
            email: ['', [Validators.required, Validators.email], this.uniqueEmailValidator.createValidator(
            this.wlService.wl.degree, this.wlService.wl.id
        )],

And I added the UniqueEmailValidator as a provider in the module and injected in this component constructor.

Upvotes: 2

Related Questions