0

I have an AnuglarJS app using ui-router and i have a http request that tells information about the user such as login state, access, language, user group, what controllers he can access and so on. It also defines behavior for some controllers and my ui-router states rely on it.

$http.get("/user").then(function(response) {
    UserService.userData = response.data.userData,
});

Now I would like to delay all of the ui-router state change AND delay Services to be loaded if possible. And only allow anything to happen in the angular app after the request returned. So I can either redirect the user to the login page or load the proper templates, or do whatever i want inside the application based on the information received.

I would like this to apply to multiple routes with possibly adding exceptions.

So the goal i would like to achieve is to delay the angular.js app being loaded, wait for the http request to finish then continue the application.

3
  • Please check my answer, does it work for you? Because it's a correct title which will help other users in the internet to find this solution to get some help. Commented Apr 3, 2017 at 9:50
  • I don't think so. This question is related to ui-router and not general $http - if users will search AngularJS wait for $http request they will check this question/answer an they will see its nothing they searched for. Commented Apr 3, 2017 at 9:53
  • 2
    @Azarus You can't 'delay every AngularJS thing', and if you're trying to do that, you have XY problem. The proper way to do this is with UI Router. There's more than one way to do this with UI Router, and the answer offers one of them. Commented Apr 6, 2017 at 17:30

2 Answers 2

1

Whenever my app has dependencies on pre-loadeded data, be it user details, permissions, or whatever else, I nest all the apps routing under a single root route, and use the resolve option for it.

For example, here is a piece of code that loads the user settings for locale, and then initializes my localization service that will go on to translate the entire app:

routeConfig.$inject = ['$stateProvider', '$urlRouterProvider'];
function routeConfig($stateProvider, $urlRouterProvider) {
    $urlRouterProvider
        .otherwise("/");

    $stateProvider
        .state('root', {
            url: '',
            abstract: true,
            template: '<div navbar></div><div ui-view class="container"></div>',
            resolve: {
                dependencies: globalAppDependencies
            }
        })
        .state('root.index', {
            url: '/',
            template: '<div main-page></div>'
        })
        .state('root.showcase', {
            url: '/showcase',
            template: '<div showcase-page></div>'
        });

    globalAppDependencies.$inject = ['userRepository', 'myLocalization'];
    function globalAppDependencies(userRepository, myLocalization) {
        return userRepository.login()
            .then(function (user) {
                return myLocalization.setLocale(user.locale);
            });
    }
}

Note: the root route template holds a ui-view, in addition to the one in the html template. This is required, since all the app views are nested below root.

My app template looks like this:

<div ng-app="my-application">
    <div application-loader></div>
    <ui-view></ui-view>
</div>

The application-loader is used for an animation while we're fetching the user settings and the localization files.

Sign up to request clarification or add additional context in comments.

10 Comments

So every sub-state is going to wait until the resolve promise is finished?
Yes. Whatever other routes you may have, the state should always be root.*. If you have public pages that don't depend on user data, like a login screen perhaps, then you shouldn't nest it under root
I'm not sure that this will work. IIRC dependencies resolver won't be executed automatically in nested states. Is there a plunk that can replicate this example properly?
Child states will inherit resolved dependencies from parent state(s), which they can overwrite. You can then inject resolved dependencies into the controllers and resolve functions of child states. According to the docs: github.com/angular-ui/ui-router/wiki/…
@Azarus Yes. This means that this solution requires to inject dependencies resolver into each nested state, see github.com/angular-ui/ui-router/wiki/… . And if a dev forgets to do that, the resolver just doesn't run.
|
1

You could achieve this with $stateChangeStartdefault event provided by ui-router. It get called before any route change. You could put this stuff in the run() part of your main application module define object.

/**
 * Before state change / page switch
 */
$rootScope.$on("$stateChangeStart", function (event, toState, toParams, fromState, fromParams, options) {

   //prevent default route change if user is not available in factory/service.
   if ((!UserService.userData || !UserService.userData.loggedIn) 
          && toState.name !== 'login') {

        event.preventDefault();

        $http.get("/user").then(function(response) {
            if (response.data.loggedIn) {
                UserService.userData = response.data.userData;
                if (toState.name !== fromState.name) {
                   $state.go(toState.name, toParams, options);
                }
            } else {
                $state.go('login');
            }
        });
   }
});

Hint: It would be much better to not fire a HTTP-Request on any route-change just for checking the login session state. You could store the session state into localStorage which brings you performance push. Check this session state on any request which recives data from a secured route in your backend. If the session is not active any more, redirect the user in the moment he tries to access this data.

5 Comments

Wouldn't this fetch user data for every single state navigation in the application, and delay the actual navigation till that's loaded?
Not to mention, this isn't the correct answer. @Azarus had problem X - he wanted to wait for user data to be loaded before anything else worked. He approached it with Y - trying to manipulate $http. You @lin gave him an implementation for Y, while I gave a solution to X.
Actually your code does completely something else. It performs a $http request everytime you change a ui-router state, and then redirects to a completely different state. So your code is uncomplete and not helpfull. And it's redundant to perform such a http request every time you change a state not? I want to perform it only once, when the app starts up the very first time. Not everytime.
too much recursion, it doesnt wait for the $http call to finish.
Great finally works :) But sadly it's still complicated to exclude states, and using ui-router dependency resolver turns out to be a better solution. And i would recommend using that for such thing.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.