Button only works on second click because of asynchronous promise?


#1

I have a login button that receives a username and a password and checks against a local SQL storage - where the username, password and a token are stored previously, from a web server.

The problem is, the validation only seems to work on the second time you press the button, as it seems that the promise hasn’t resolved yet. I should add, the button can be pressed on the first time as usual, but it just opens the alert.

This is what gets called on click:

login(userCreds){
    this.user.validate(userCreds.name,userCreds.password).then((isValid) => {
        this.isValid = isValid;
    });
    if(this.isValid){
        this.nav.setRoot(MainPage);
    }
    else{
        let alert = Alert.create({
            title: this.translate.instant('error'),
            buttons: ['OK']
        });
        this.nav.present(alert);
    }
}

And this is the validate function it calls:

validate(name,password){
    var isValid = false;
    if (!this.userSQL){
        return Promise.resolve(isValid);
    }
    return this.userSQL.query('SELECT * FROM user WHERE name = "'+ name +'"').then((data) => {
        if(password == data.res.rows.item(0).password){
            isValid=true;
        }
        return isValid;
    }, (error) => {
    });
  }

The table where the user data is stored only stores a single user at a time, so I add a blank one at initialization and all subsequent changes are just updates and that part’s working fine, but I’m sure I can do a check on the password in a more…correct way.


#2

I would look into making an asynchronous validator. Here’s an example.


#3

Okay, while I understand that I should be using a validator - wasn’t aware about them in ionic - am I not just going to run into the same thing, since I’m going to submit data when pressing the button anyway?


#4

I don’t understand what you’re saying, but asynchronous validators return promises, so you can retrieve things from storage as part of the validation process. Why are you doing all this in the first place, though? Why do you need to store user credentials on the device? That’s just asking for trouble.


#5

Isn’t what I’m doing essentially working in the same way as a validator? It’s not a validator, but it’s returning a promise that, as far as I understand, would work the same way and, since the form validation would happen on clicking the button, wouldn’t it run into the same issue that I’m already having?

Edit: I want to make it clear, I know I should be using a validator here, but isn’t there anything wrong with my code that’d cause it to still be wrong if I use a validator?

The app requires an internet connection to sync, but then can be used while offline; resyncing the data, to send the local changes to the web or getting new data, requires an internet connection. If you close the app and want to do new offline work, I still want the user to login again, however.


#6

The call to validate() returns a promise immediately. Until that promise resolves, this.isValid is unreliable. You’re barreling through relying on this.isValid before it is set. The asynhcronous validator harness won’t do this, so the problem should go away.

If you do insist on storing user credentials locally, I would strongly suggest hashing passwords with something like bcrypt.


#7

Alright, I’ll look into validators then; I assumed I’d run into the same issue concerning the isValid promise; which is why I was wondering if there was some sort of way to resolve the promise before reaching that if().

And yes, I didn’t mention, but I’m going to hash the password - wasn’t the highest priority right now, that’s all.