Liam M
Liam M

Reputation: 5432

Access 'this' Inside Promise

In the typescript function below, 'this' doesn't resolve to the instance of EmailValidator. How can I correct this function so it resolves to the correct instance of EmailVaildator and in turn, so that I can access _registerServices?

class EmailValidator {

    constructor(private _registerServices: RegisterServices) { }

    isAvailable(c: AbstractControl): Promise<ValidationResult> {
        let q = new Promise((resolve, reject) => {
            this._registerServices.emailIsAvailable(antiForgeryToken(), c.value)
                .then(result => {
                    // Need to actually check the result.
                    resolve({ "emailtaken": true })
                },
                error => {
                    // Need to communicate the server error? Probably not.
                    resolve({ "servererror": true })
                });
        });

        return q;
    }
}

Upvotes: 8

Views: 8220

Answers (4)

Roman
Roman

Reputation: 79

I would offer to write it a bit different

class EmailValidator {

    constructor(private _registerServices: RegisterServices) { }

    isAvailable(c: AbstractControl): Promise<ValidationResult> {
        return this._registerServices.emailIsAvailable(antiForgeryToken(), c.value)
            .then(result => {
                // Need to actually check the result.
                return { "emailtaken": true }
            })
 // shorter .then(result => ({ "emailtaken": true }))
            .catch(error => {
                // Need to communicate the server error? Probably not.
                return { "servererror": true }
            });
 // shorter .catch(error => ({ "servererror": true }))

        });

    }
}

Upvotes: 0

Abdulrahman Alsoghayer
Abdulrahman Alsoghayer

Reputation: 16540

You have the problem because you are passing the value of isAvailable which is a function. You are not executing it, you are just passing the reference to the function.

One way to solve it is as in @Thilo's answer

Another way is to assign isAvailable to a lambda expression instead of a function. like this:

class EmailValidator {

    constructor(private _registerServices: RegisterServices) { }

    isAvailable = (c: AbstractControl): Promise<ValidationResult> => {
        let q = new Promise((resolve, reject) => {
            this._registerServices.emailIsAvailable(antiForgeryToken(), c.value)
                .then(result => {
                    // Need to actually check the result.
                    resolve({ "emailtaken": true })
                },
                error => {
                    // Need to communicate the server error? Probably not.
                    resolve({ "servererror": true })
                });
        });

        return q;
    }
}

Upvotes: 1

Thilo
Thilo

Reputation: 262464

You are losing this, because you are passing around the isAvailableEmail as a "raw" function here:

email: ['', Validators.required, this._emailValidator.isAvailableEmail]

You can fix this by binding it to this (using the fat arrow):

email: ['', Validators.required,
  (control) => { this._emailValidator.isAvailableEmail(control) }
]

Upvotes: 9

Liam M
Liam M

Reputation: 5432

It turned out that the 'this' reference was undefined even if it was used as follows:

class EmailValidator {

    constructor(private _registerServices: RegisterServices) { }

    isAvailable(c: AbstractControl): EmailValidator {
        return this; // 'This' is undefined!
    }
}

I gather this has something to do with how the method was called, perhaps passing a non-static method where a static method was expected:

...
this.registerForm = fb.group({
    email: ['', Validators.required, this._emailValidator.isAvailableEmail],
    password: ['', Validators.compose([Validators.required, Validators.minLength(8)])],
    phoneNumber: ['', Validators.required],
    country: ['', Validators.required]
    });
...

If someone could offer some guidance on what's occurring here, that'd be fantastic.

My Solution

I reordered my code and produced the following:

class EmailValidator {

    static isAvailableEmail(services: RegisterServices): (AbstractControl) => Promise<ValidationResult> {
        let g = (c: AbstractControl) => {
            return new Promise((resolve, reject) => {
                services.emailIsAvailable(antiForgeryToken(), c.value)
                    .then(result => {
                        // Need to actually check the result.
                        resolve({ "emailtaken": true })
                    },
                    error => {
                        // Need to communicate the server error? Probably not.
                        resolve({ "servererror": true })
                    });
            });
        };

        return g;
    }
}

And amended its usage:

...
this.registerForm = fb.group({
    email: ['', Validators.required,
        EmailValidator.isAvailableEmail(this._registerService)],
    password: ['', Validators.compose([Validators.required, Validators.minLength(8)])],
    phoneNumber: ['', Validators.required],
    country: ['', Validators.required]
    });
...

Which works correctly.

Upvotes: 2

Related Questions