Ionic 2 Login With Parse API - Context Problem THIS - returns different class within success and error functions

Hi guys, this is my first post here. I have looked for similar topics but here is an issue I have been trying to get my head around for 2 days.

I have implemented an integration between the Parse API and my first Ionic2 project in order to log users in.
Here is what an authentication call looks like and also the problems.

Attempt 1:

  user = Parse.User.logIn(this.login.username, this.login.password, {
        success: function(user) {
              // Do stuff after successful login.
             this.nav.push(TabsPage); // HERE I WANT TO REDIRECT TO THE LOGGED IN PAGES OF MY APP
        },
       error: function(user, error) { // HERE I WANT TO SHOW AN IONIC 2 ALERT WITH THE ERROR DETAILS
            // The login failed. Check error to see why.
            let alert = Alert.create({
            title: 'Error',
            subTitle: 'Login / Password are incorrect. If you don't have an account you can register through Facebook also.',
            buttons: ['Ok!']
          });
       this.nav.present(alert);
       } 
  });

The problem I am having is that inside the success and error functions the (this), which in my case stands for the export class LoginPage (Class representing the LoginPage in Angular2), is an object from a class different than the Angular2 Context. Basically, I can’t access any outside elements from the Angular2 exported class within the success and error handlers.

I just don’t know why this is happening. I have also tried to ignore the success and error handlers, and using the returned value of the login method from the Parse API, which works (sort of). But their API returns a Promise which is filled with a valid User WHEN the request ends, which makes my app always pop up the error alert message and then logging the user in.

Attempt 2:

  user = Parse.User.logIn(this.login.username, this.login.password, {});
  if(user != null){
    this.nav.push(TabsPage);
  } else {
    let alert = Alert.create({
      title: 'Error',
      subTitle: 'Login / Password are incorrect. If you don't have an account you can register through Facebook also.',
      buttons: ['Ok!']
    });
    this.nav.present(alert);
  } 

Has anybody here implemented an integration between Parse and Ionic2 successfully already?

Thanks in advance.
Arthur.

Hi, i know nothing about the Parse API, but let me tell you, your issue is probably related to how closures work in JS, there are 3 ways to fix this, the first is a good practice to get and the third is a fix available in ES6:

1 . When you call a function the context of this changes due to the async nature of JS, so in an async call this could refer to the window object instead of the class that contains the call to the async function, to fix this a good practice is to do this, you could use other names like self, that or another but i prefer this_ref:

var this_ref = this
user = Parse.User.logIn(this_ref.login.username, this_ref.login.password, {
  success: function(user) { this_ref.nav.push(TabsPage)}, error: ......

The trick here is to bind the this object to a variable before getting to the async call, thus binding before the context of this change, this_ref is available through all the function due to the magic of closures.

2 . Use the bind(this) function to bind the call of the callbacks to the context of the right this, i don’t recommend this approach because it’s inconsistent (i mean you will use it some times but not all the time) and could be heavily misleading about the context of this you are using.

3 . ES6 fat arrow functions fix this issue by auto-binding to the scope of the parent scope, i quote:

Arrow functions serve two main purposes: more concise syntax and sharing lexical this with the parent scope. Let’s examine each in detail. From strongloop intro to ES6 fat arrow functions

Basically you just need to replace your functions with fat arrow functions, though idk if it will work as you are using an API in between so the °1 is still my way to go and i encourage you to do so, here’s how it would look with only fat arrow:

user = Parse.User.logIn(this.login.username, this.login.password, {
    success: user => this.nav.push(TabsPage), // Implicit return, use parenthesis if more than one arg
   error: (user,  error) =>  { .......
3 Likes

Awh Yeah man! Dead on! It works like a charm thank you very much for your explanation.

I ended up going with alternative 1. First, because it is so freaking simple that I really thought “How come I didn’t think of that?”

I think that the most elegant solution could be 3, but anyways. Problem solved.

Thanks.

I face-smashed with this when first started with Angular and async JS 6 months ago, that neat °1 have saved me a lot of headaches, which name did you chose for the reference variable?

Btw self could colide with some properties, that’s why i didn’t use it.

1 Like

You can also use the bind function to bind the context to a specific reference. I find generating a self variable to be a bit of a hack and can still cause problems when your callbacks are calling other callbacks. In the case of the presented code, it would look something like:

success: function(user) {
          // Do stuff after successful login.
         this.nav.push(TabsPage); // HERE I WANT TO REDIRECT TO THE LOGGED IN PAGES OF MY APP
    }.bind(this)

I think the current best practice though is the lambda (fat-arrow) notation you were suggesting.

It’s actually the #2 item in my first answer, i don’t like it so i didn’t bother to put an example.

Overlooked it somehow. Sorry about that.

Thanks for your detailed answer… helped me!

1 Like