Timo
Timo

Reputation: 738

Angular state change after a promise

I'm not sure if this is a duplicate or not, but I didn't manage to find anything that worked for me, so I'm posting this question.

I have a situation where I need to get values from database before directing user to certain routes, so I could decide what content to show.

If I move e.preventDefault() right before $state.go(..) then it works, but not properly. Problem is that it starts to load default state and when it gets a response from http, only then it redirects to main.home. So let's say, if the db request takes like 2 seconds, then it takes 2 seconds before it redirects to main.home, which means that user sees the content it is not supposed to for approximately 2 seconds.

Is there a way to prevent default at the beginning of state change and redirect user at the end of state change? Also, if we could prevent default at the beginning of state change, then how could we continue to default state?

(function(){
    "use strict";
    angular.module('app.routes').run(['$rootScope', '$state', '$http', function($rootScope, $state, $http){
        /* State change start */
        $rootScope.$on('$stateChangeStart', function(e, to, toParams, from, fromParams){
            e.preventDefault();
            $http
                .get('/url')
                .error(function(err){
                    console.log(err);
                })
                .then(function(response){
                    if( response.data === 2 ){
                       // e.preventDefault()
                       $state.go('main.home');
                    }
                    // direct to default state
                })
        }
    }]);
});

Upvotes: 0

Views: 267

Answers (2)

Keammoort
Keammoort

Reputation: 3075

You could add a resolve section to your $stateProviderConfig.

Inside the resolve you can make a request to the databse and check required conditions. If case you don't want user to acces this page you can use $state.go() to redirect him elsewhere.

Sample config:

.state({
    name: 'main.home',
    template: 'index.html',
    resolve: {
        accessGranted: ['$http', '$state', '$q',
            function($http, $state, $q) {
                let deffered = $q.defer();
                $http({
                    method: 'GET',
                    url: '/url'
                }).then(function(data) {
                    if (data === 2) {
                        // ok to pass the user
                        deffered.resolve(true);
                    } else {
                        //no access, redirect
                        $state.go('main.unauthorized');
                    }
                }, function(data) {
                    console.log(data);
                    //connection error, redirect
                    $state.go('main.unauthorized');
                });
                return deffered.promise;
            }
        ]
    }
});

Documentation of the resolve is available here

Note that you could use Promise object instead of $q service in case you don't need to support IE

Upvotes: 1

Mathews
Mathews

Reputation: 909

One way to handle this situation is adding an interceptor as follows.

.config(function ($httpProvider) {
        $httpProvider.interceptors.push('stateChangeInterceptor');
    }).factory('stateChangeInterceptor', function ($q, $window,$rootScope) {
        return  {
            'response': function(response) { 
                var isValid = true;//Write your logic here to validate the user/action. 
                /*
                * Here you need to allow all the template urls and ajax urls which doesn't 
                */
                if(isValid){
                   return response;
                }
                else{
                  $rootScope.$broadcast("notValid",{statusCode : 'INVALID'});
                }

            },
            'responseError': function(rejection) {
              return $q.reject(rejection);
            }
        }
    })

Then handle the message 'notValid' as follows

.run(function($state,$rootScope){
        $rootScope.$on("notValid",function(event,message){
        $state.transitionTo('whereever');
    });
})

Upvotes: 0

Related Questions