Tandis Tandis Tandis Tandis - 3 months ago 13
jQuery Question

routing to proper page in AngularJS if localStorage token exists

Here is the html:

<form method="post">
<div class="form-group">
<input type="text" ng-model="log.username" placeholder="username">
</div>
<div class="form-group">
<input type="password" ng-model="log.pwd" placeholder="password">
</div>
<div class="form-group">
<button ng-click="log.login()" class="loginSubmit">Login</button>
</div>
</form>


Here is the user service:

function userService($http, API) {

$http.defaults.headers.post['Content-Type'] = 'application/x-www-form-urlencoded;';
$http.defaults.transformRequest = [function(data) {
return angular.isObject(data) && String(data) !== '[object File]' ? param(data) : data;
}];

var self = this;

self.login = function(username, pwd, ctrl) {
ctrl.requestdata = API + '/winauth' + '; with ' + username;
return $http.post(API + '/winauth', {
username: username,
pwd: pwd
})
};
}


EDIT - Here is the controller:

function LoginCtrl(user, $state) {
var self = this;

function handleRequest(res) {
self.responsedata = res;
self.message = res.data.message;

var authToken = res.data.auth_token;

// ** ADDED THIS SECTION **
if(authToken){
localStorage.setItem('token', authToken);
}

}

self.login = function() {
this.requestdata = 'Starting request...';
user.login(self.username, self.pwd, self)
.then(handleRequest, handleRequest)

var token = localStorage.getItem('token');

// ** CHANGED THIS SECTION **
if(!token){
$state.go('login');
console.log('token undefined');
}
else if(token){
console.log('token defined');
$state.go('dashboard');
}
}
}


What I want to do is if the 'token' on their local storage doesn't exist (null) or has a value of "undefined" I need it to go back to the login page. If the 'token' does exist and is defined with a value then go to the dashboard.

What is happening now is that when you don't enter a username or password and click the login button it logs in the console "token undefined" and a message comes back from the API "403 forbidden" and drops a key "token" with a value of "undefined" on the local storage. So far so good. When you click the login button a second time with all the fields empty it sends me to the dashboard page and logs in the console "token defined" yet all the API calls on the dashboard show errors "403" obviously cause I don't have a valid token...Not so good.

Now when I clear all that out,start fresh and enter the correct credentials in the form and click the login button it logs in the console "token undefined" doesn't route me to the dashboard yet it puts a key "token" with a value of "24hgkkd875hhs887g" and it just stays on the login page. Now when I click the button the second time it logs in the console "token defined" and sends me to the dashboard like it is supposed to and all the JSON gets loaded properly.

Its seems like it kind of works but doesn't. Is this happening because the "handleRequest" function is setting the token in their local storage and the "login" function inside the controller is running to soon to grab the result of the "localStorage.setItem('token', authToken);"

EDIT:

As per @noKid suggestion I added a check to see if the value coming back from the API is undefined and if so it will not put a key in the local Storage. So now I just use [ if(!token){ } else if(token){ } (see my edits above).

When they put in the wrong credentials no token is added to local storage. Now when you put in the correct credentials and click login it adds the token to local storage but doesn't go to the dashboard it just stays there. but when you click the login button a second time it takes you to the dashboard. Is this because the IF statements are finishing before the .then(handleRequest, handleRequest) is finished or do I need to put the IF statements somewhere else?

Answer

If I'm not mistaken here, it comes from the fact that localStorage only stores key/values as strings. (An undefined value is stored as the 'undefined' string).

To be certain, with a quick JSfiddle demo, we can see that the result of

localStorage.getItem('token')

is not undefined as a value, but 'undefined' as a String.

I think your best bet here is to prevent the token from being set if authToken is undefined, or to check if the token value is not 'undefined' :

var authToken = res.data.auth_token;

// Local storage will be updated only if authToken is not null or undefined
if(authToken)
    localStorage.setItem('token', authToken);

or

var token = localStorage.getItem('token');

// It should work, but I think it's far less comprehensive
if(typeof token === 'undefined' || token === null || token === 'undefined'){

Edit:

Your new problem seems to happens because of Promises.

user.login(self.username, self.pwd, self)
  .then(handleRequest, handleRequest)

will call the handleRequest() method once the $http.post has been done.

In the mean time, the remaining code in the function will execute.

So var token = localStorage.getItem('token'); will likely be executed before the call to handleRequest(), you will be redirected to the login page, BUT handleRequest() will still be executed after that, thus sending you to the dashboard on the second login.

I recommend you to change it like this:

user.login(self.username, self.pwd, self)
    .then(function(res){
        // We first store the token in the localStorage
        handleRequest(res);

        // Then we can safely use it.
        var token = localStorage.getItem('token');

        // ** CHANGED THIS SECTION **
        if(!token){
          $state.go('login');
          console.log('token undefined');
        }
        else if(token){
          console.log('token defined');
          $state.go('dashboard');
        }
    }, function(){
        console.log('This method will be called if the $http post fails');
    })

Also, I strongly advise you to learn Promises at it will help you for asynchronous stuff.

Comments