3

I'm using ui-router in my angular application. Currently I've two routes /signin & /user. Initially it shows /signin when the user clicks on the login button, I'm sending a ajax request and getting the user id. I'm storing the user id in localstorage and changing the state to /user.

Now, what I want, if a user is not loggedin, and user changes the addressbar to /user, it'll not change the view, instead it'll change the addressbar url to /signin again.

I'm try to use resolve, but it's not working. My code is:-

module.exports = function($stateProvider, $injector) {
$stateProvider
.state('signin', {
    url: '/signin',
    template: require('../templates/signin.html'),
    controller: 'LoginController'
})
.state('user', {
    url: '/user/:id',
    template: require('../templates/user.html'),
    resolve:{
        checkLogin:  function(){
             var $state = $injector.get('$state');
            console.log("in resolve");
             if (! window.localStorage.getItem('user-id')) {
                 console.log("in if")
                  $state.go('signin');
             }
        }
    },
    controller: 'UserController'
 })

}

Please help me to solve this problem.

Indranil Mondal
  • 2,799
  • 3
  • 25
  • 40

2 Answers2

10

I don't think it's allowed to change states in the middle of a state transition.

So, the way to address it is to have the checkLogin resolve parameter (I changed it below to userId) to be a function that either returns a value or a promise (in this case, a rejected promise, if you can't get the user-id).

You'd then need to handle this in $rootScope.$on('$stateChangeError') and check the error code.

resolve: {
   userId: function ($q, $window) {
      var userId = $window.localStorage.getItem('user-id');
      if (!userId) {
         return $q.reject("signin")
      }

      return userId;
   }
}

And redirect in the $stateChangeError handler:

$rootScope.$on('$stateChangeError', function (event, toState, toParams, fromState, fromParams, error) {
     if (error === "signin") {
        $state.go("signin");
     }
});
New Dev
  • 48,427
  • 12
  • 87
  • 129
  • Thanks, it's really helpful. But suppose I've to do this kind of things multiple times in the app. Say there are some more routes, and I want to change the route depending on certain case. Say similarly if user is logged in he'll not be able to go to login & forget password page. So for all these cases I've to navigate in the rootscope. So I've to add some if-else or switch case and depending on that change the state. So for large app it'll be a huge block. So can I do this thing in the state provider itself so that I don't have to add those if-else. – Indranil Mondal Dec 19 '14 at 19:54
  • You don't need to navigate to rootscope - you'd just need to reject the navigation and handle different errors by redirecting appropriately. – New Dev Dec 19 '14 at 20:31
  • You've written in the statechangeerror of rootscope if (error === "signin") { $state.go("signin"); } So have done the redirecting using state.go in rootscope. Now suppose if I want to do the same like, if user is logged in he'll not be able to go to login & forget password page. So I'll write else if (error === "user") { $state.go("user"); } Something like this. So can I avoid this multiple in blocks? Please correct me if I'm wrong. – Indranil Mondal Dec 20 '14 at 18:10
  • The `$rootScope.$on` statement can be written in `app.run` – New Dev Dec 20 '14 at 18:20
3

If someone has this problem, you can solve it, using timeout service. It will put state switching call at the end of queue.

Also, you should use promises. Rejecting it will prevent initialization of that state:

resolve:{
    checkLogin: function(){

         var deferred = $q.defer();

         var $state = $injector.get('$state');
         if (!window.localStorage.getItem('user-id')) {
             $timeout(function(){$state.go('signin');});
             deferred.reject();
         } else {
             deferred.resolve();
         }

         return deferred.promise;
    }
},
Dee
  • 43
  • 6