3

I need to initialize my Angular with some User data that the whole app depends on. Therefore I need the initialization to be resolved before the router kicks in and controllers are initialized.

Currently, I wrote the initialization code in a run() block of the angular module. The initialization involves an asynchronous http request to get user data and the rest of the application relies upon the user data. How can I ensure that the http request is resolved before the router kicks-in initializing the controllers?

I am using the ui-router.

The initialization consists in the following: 1) get cookie 'userId' 2) get User from server (asynchronous http request, the whole app depends upon the User) 3) set authService.currentUser

this is a sample of the code

.run(['$cookies', 'userApiService', 'authService', 
  function($cookies, userApiService, authService){

    var userId = $cookies.get('userId');
    userId = parseCookieValue(userId);

    userApiService.getOne(userId).then(function(user){
      authService.currentUser = user;
    });
}])
.config(['$stateProvider', '$urlRouterProvider', '$locationProvider', 
  function($stateProvider, $urlRouterProvider, $locationProvider) {

    $locationProvider.html5Mode(true);

    $urlRouterProvider.when('/', '/main');

    $stateProvider
      .state('login', {
        url: '/login',
        views: {
          'header': {
            templateUrl: 'views/header.html',
            controller: 'HeaderCtrl'
          },
          'content': {
            templateUrl: 'views/login.html',
            controller: 'LoginCtrl'
          },
          'footer': {
            templateUrl: 'views/footer.html',
          }
        }
      })
      .state('main', {
        url: '/main',
        views: {
          'content@': {
            template: '',
            controller: function($state, authService) {
              if(!authService.isAuthenticated()) {
                $state.go('login');
              }
              if(authService.isStudent()) {
                $state.go('student');
              }
              if(authService.isAdmin()) {
                $state.go('admin');
              }
            }
          }
        }
      })
      .state('student', {
        url: '/student',
        views: {
          'header@': {
            templateUrl: 'views/header.html',
            controller: 'HeaderCtrl'
          },
          'content@': {
            templateUrl: 'views/student.html',
            controller: 'StudentCtrl'
          },
          'footer@': {
            templateUrl: 'views/footer.html',
          }
        }
      })
      .state('admin', {
        url: '/admin',
        views: {
          'header@': {
            templateUrl: 'views/header.html',
            controller: 'HeaderCtrl'
          },
          'content@': {
            templateUrl: 'views/admin.html',
            controller: 'AdminCtrl'
          },
          'footer@': {
            templateUrl: 'views/footer.html',
          }
        }
      })
}])
klode
  • 10,821
  • 5
  • 34
  • 49
  • 3
    Look into the `resolve` property. You can define it on the root state – New Dev Aug 18 '15 at 18:19
  • Thanks @NewDev, could you give me some guideline to define the root state? For the moment I added the resolve property to the `main` state, but if I understood you correctly this is not the root state, correct? -- I am new to ui-router -- – klode Aug 18 '15 at 20:15
  • Also @NewDev, could you explain how to pass a parameter to `resolve` property? For instance, I want to display student information with matching `student_id`. Thank you. – Shaohao Aug 18 '15 at 20:20
  • @ShaohaoLin if you have another question, it would be better to ask in a new separate question – klode Aug 18 '15 at 20:41
  • @klode, I meant by "root" that it's the state that all other states (that require the user id) descend from, since child states inherit the parent's resolved values. – New Dev Aug 18 '15 at 20:51
  • @NewDev I understand now, since I did not have a parent state in the example code I was confused. – klode Aug 18 '15 at 21:01
  • this answer is a nice summary of possible solutions http://stackoverflow.com/questions/27050496/run-controllers-only-after-initialization-is-complete-in-angularjs – klode Aug 18 '15 at 21:58

1 Answers1

2

Expanding on someone's comment, you can create a root state that is a parent to all of your other app's states (children to the root). The root state resolves all the user data and then you can inject the user data to any controller or store it in a service.

$stateProvider
  .state('root', {
    url: '',
    abstract: true,
    template: '',     // some template with header, content, footer ui-views
    resolve: {
      // fetch user data
    }
  })
  .state('root.login', {
    url: '/login',
    views: {
      'header': {
        templateUrl: 'views/header.html',
        controller: 'HeaderCtrl'
      },
      'content': {
        templateUrl: 'views/login.html',
        controller: 'LoginCtrl'
      },
      'footer': {
        templateUrl: 'views/footer.html',
      }
    }
  })
  .state('root.main', {
    url: '/main',
    views: {
      'content@': {
        template: '',
        controller: function($state, authService) {
          if(!authService.isAuthenticated()) {
            $state.go('login');
          }
          if(authService.isStudent()) {
            $state.go('student');
          }
          if(authService.isAdmin()) {
            $state.go('admin');
          }
        }
      }
    }
  })

  ...  // your other states

The key is that all of your app states must be a child of your root state i.e. root.<name> in your state declaration. This will ensure that no other controller starts until your user data is available. For more information on resolve and how to use it read here. Also, parent and child states.

allienx
  • 877
  • 6
  • 10