Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to Inject a service into an async validator?

Tags:

angular

I have two very similar async validators. Both check values for uniqueness: Email and initials. The first one looks like this:

public static createEmailUniqueValidator(userService: UserService, originalEmailAddressFn: () => string) {
    return (control: AbstractControl): Observable<ValidationErrors> => {
        const originalEmailAddress: string = originalEmailAddressFn();
        if (originalEmailAddress && originalEmailAddress === control.value) {
            return Observable.of({});
        }
        return Observable.timer(1000).switchMap(() => {
            return userService
                .isEmailAddressUnique(control.value)
                .map(result => (result ? null : { 'This email address is already in use': true }));
        });
    }
}

The second one almost identical, except for the method it calls on the userService. How can I make one generic validator factory and pass it the method it should call to do the actual checking ?

So far, I have this:

public static createValidator(uniqueFn: (value: string) => Observable<Boolean>, originalEmailAddressFn: () => string, errorMessage: string) {
        return (control: AbstractControl): Observable<ValidationErrors> => {
            const originalEmailAddress: string = originalEmailAddressFn();
            if (originalEmailAddress && originalEmailAddress === control.value) {
                return Observable.of({});
            }
            return Observable.timer(1000).switchMap(() => {
                return uniqueFn(control.value)
                    .map(result => (result ? null : { errorMessage: true }));
            });
        }
    }

But I am getting the following error:

ERROR TypeError: Cannot read property 'restService' of undefined at webpackJsonp.../../../../../src/app/services/user/user.service.ts.UserService.isEmailAddressUnique (user.service.ts:108)

restService is another service that is injected in the userService. It looks like the dependencies have not been resolved. How can I fix this ?

This is my userService:

// imports ...

@Injectable()
export class UserService {

  // [...]

  constructor(private restService: RestService) { }

  isEmailAddressUnique(emailAddress: string): Observable<Boolean> {
    return this.restService.get(this.usersUrl + '/validateUniqueEmailAddress/' + emailAddress);
  }
}
like image 550
Tim Avatar asked Sep 14 '17 14:09

Tim


People also ask

What is asynchronous validator?

The async validator's validate method returns a Promise that resolves if validation passes and value is updated or rejects with an Error if validation does not pass and value is not updated. The async validator also has a hint field that returns a Promise that when resolved will return a hint.

What is asynchronous validation in angular?

Angular does not provide built-in type async Validation implmentation, it provides only for sync validation. The implementation of async validator is very similar to the sync validator. The only difference is that the async Validators must return the result of the validation as an observable or as Promise.

How do I create a custom validator?

in order to implement a custom validation directive, we need to implement the Validator interface, which only has the validate method. the validate method is going to call the validation creation function, and pass the control reference (the password control in this case) to the validator.

Can you give an example of built-in Validators?

Validators are used to ensure that the values in a form meet certain requirements. They are available to Template-Driven Forms or Reactive Forms in Angular applications. There are several built-in validators like required , email , pattern , and minLength .


1 Answers

EDIT: Both misunderstood the desired number of functions, and didn't fully explain. Took the time to create a (slightly simplified) plunker that roughly matches your setup (instead of trying to check an API, it simply checks if the email is valid, but does so as an Observable; it also removes a couple parameters for createValidator to focus on the essential factory).

I've created a working version, and a non-working version. The latter successfully reproduces the error you were seeing (Cannot read property 'restService' of undefined) when you run it. See src/user.service.ts for the breaking change. And I've updated below to highlight the two functions where it's important to use the fat arrow declaration, to properly bind this. If there's something I've over-simplified in the plunk, do let me know, and we can work on a fork together.

Important functions for using fat arrow declaration:

public static = createValidator(/*same parameters*/) => {
    // same content
}

and

public isEmailAddressUnique = (/*same parameters*/) => {
    // same content
}
like image 136
Jack Koppa Avatar answered Nov 15 '22 04:11

Jack Koppa