Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I access the 'resolve' property of a UI-Router root state from $stateChangeStart?

I'm trying to implement a user auth check and access check system in my app, however I keep hitting roadblocks. I think I have it the correct way this time but I have one last hurdle.

A little background: I tried putting all of the code into the $rootScope.on($startChangeStart) and it worked, horribly... but it worked. The route was always redirected but due to the auth check on the backend it displayed the first request page for 1/2 a second and then the redirect page every time. Thus I tried 'pausing' page load by calling evt.preventDefault() right at the start of the $startChangeStart function, which worked, but trying to put the user back to the original route afterwards caused an infinite loop in the router.

So after more research and reading a lot of stack posts I'm certain that 'resolve:' is the proper place to put the auth check to ensure the page is not loading while it occurs, and then redirect the user if needed from the $startChangeStart. ($state and event are always undefined in my attempts to inject them into a resolve function) It seems like the winning combination.

My problem: I have the resolve on the root state in my app: 'main'

This was to avoid code redundancy, however I cannot determine how to access the root state's properties, and therefore the resolve result, from the $stateChangeStart function. The toState is the child state, while the fromState is either the previous state or an abstract state with the '^' route...

Do I have to put the resolve on every child state for this to work, or is there a way to access the root state from this point?

Basic app setup:

angular.module('App', ['ui.router', 'ui.bootstrap', 'ui.event', 'AngularGM', 'ngResource'])
.config(['$urlRouterProvider', '$stateProvider', function($urlRouterProvider, $stateProvider){
    $urlRouterProvider
        .when('/home', '/')
        .when('', '/')
        .when('/sign-up/joe', '/sign-up')
        .otherwise('/');

    $stateProvider
        .state('main', {
            url: '',
            abstract: true,
            templateUrl: 'views/main.html',
            controller: 'MainCtrl',
            resolve: {
                checkAccess: ['accountService', function(accountService) {
                    accountService.checkAuth(function(){
                        accountService.checkAccess(function (access){
                            return access;
                        });
                    });
                }]
            }
        })
        .state('main.home', {
            url: '',
            abstract: true,
            templateUrl: 'views/home.html',
            controller: 'HomeCtrl'
        })
        .state('main.home.index', {
            url: '/',
            templateUrl: 'views/home/index.html'
        });


.run(['$rootScope', '$state', '$stateParams', 'accountService', function ($rootScope, $state, $stateParams) {
    $rootScope.$state = $state;
    $rootScope.$stateParams = $stateParams;
    $rootScope.$on('$stateChangeStart', function(event, toState, toParams, fromState, fromParams) {
        console.dir(toState);
        console.dir(toParams);
        console.dir(fromState);
        console.dir(fromParams);
        if (!toState.checkAccess.allowed) {
            event.preventDefault();
            $state.transitionTo(toState.checkAccess.newState);
        }
    });
}]);

This is the output from the console.dir() calls on the two state objects:

Object
 name: "main.home.index"
 templateUrl: "views/home/index.html"
 url: "/"
 __proto__: Object

Object
 controller: "PlacesCtrl"
 name: "main.places.search"
 templateUrl: "views/places.html"
 url: "/places"
 __proto__: Object

Update

Oops, forgot to mention AngularJS version is v1.2.0-rc.2

$state.current console.dir()

Object
 abstract: true
 name: ""
 url: "^"
 views: null
 __proto__: Object
like image 836
Routhinator Avatar asked Oct 02 '13 16:10

Routhinator


Video Answer


2 Answers

Yes, I believe you can access root state from the $stateChangeStart function.

When using pure AngularJS I normally use current.$$route

For example, using the following route

.when('/home', {
  title:'Home',
  bodyClass: 'meetings',
  controler: 'HomeCtrl'
})

I can access the root state like so

  $rootScope.$on('$routeChangeSuccess', function (event, current, previous) {

   if (current.$$route) {

     $rootScope.title      = current.$$route.title;

     $rootScope.bodyClass  = current.$$route.bodyClass;
   }

 });

Using ui-router it's just a bit different as it's called $state.current. And you can access all the properties associated to whatever route you hit (e.g: $state.current.url)

So on your code you could have something like this

  .run(['$rootScope', '$state', '$stateParams', function ($rootScope, $state, $stateParams) {

      $rootScope.$on('$stateChangeStart', function(event, toState, toParams, fromState, fromParams) {
          console.log($state.current.url);
      });
  }]);
like image 101
Denison Luz Avatar answered Oct 17 '22 21:10

Denison Luz


You need not to use resolve. Take a look at my solution:

app.run ($rootScope, $state, Auth, ngDialog) ->
  $rootScope.$on '$stateChangeStart', (e, to) ->
    if to.authRequired and not Auth.isAuthenticated()
      e.preventDefault()
      Auth.currentUser().then(
        (user) ->
          $state.go(to)
        (failure) ->
          $rootScope.storedState = to
          $state.go('main')
          ngDialog.closeAll()
          ngDialog.open
            template: 'modals/login.html'
            controller: 'loginCtrl'
            className: 'ngdialog-theme-default'
      )

I use angular_devise and ngDialog but they are optional and you can implement it with your own user's service.

like image 25
Anton Orel Avatar answered Oct 17 '22 23:10

Anton Orel