angular ui-router: get $state info of toState in resolve

Update: this should be possible in angular-ui-router as of 1.0.0alpha0. See the release notes https://github.com/angular-ui/ui-router/releases/tag/1.0.0alpha0 and the issue https://github.com/angular-ui/ui-router/issues/1018 I created.

I would like to access the state's name and other attributes the app is navigating to using angular ui-router when working on the resolve.

The reason: I want load some user data (including their access rights) asynchronously before allowing the app the enter that page.

Currently this is not possible because injecting $state into the resolve points to the state you're navigating away form, not to the one you're navigating to.

I know I can:

  • get the toState somewhere else with $rootScope('$stateChangeStart') and save it in my settings service for instance. But I think it's a little messy.
  • hard code the state into the resolve, but I don't want to reuse my resolve for all pages

I also created an issue on the ui-router github (Please + 1 if you are interested!): https://github.com/angular-ui/ui-router/issues/1018

Here's my code so far. Any help appreciated!

.config(function($stateProvider) {

    $stateProvider.state('somePage', {
        // ..
        resolve: {
            userData: function($stateParams, $state, Settings) {
                return Settings.getUserData() // load user data asynchronously
                    .then(function (userData) {
                        console.log($stateParams);
                        console.log($state);
                        // Problem: $state still points to the state you're navigating away from
                    });
            }
        }
    });
});

Update for Ui-Router 1.x

$provide.decorator('$state', ($delegate, $transitions) => {
    $transitions.onStart({}, (trans) => {
      $delegate.toParams = trans.params()
      $delegate.next = trans.to().name
    })
    return $delegate
  })

Ui-Router 0.x

You can always decorate $state with next and toParams properties:

angular.config(function($provide) {
  $provide.decorator('$state', function($delegate, $rootScope) {
    $rootScope.$on('$stateChangeStart', function(event, state, params) {
      $delegate.next = state;
      $delegate.toParams = params;
    });
    return $delegate;
  });
});

And use as such:

.state('myState', {
    url: '/something/{id}',
    resolve: {
        oneThing: function($state) {
            console.log($state.toParams, $state.next);
        }
    }
});

So I discovered the answer to this myself. If you're code is behaving like mine, the $stateParams object is properly injected, but $state is an empty (or old) state object.

What worked for me was referencing this in the resolve function:

.state('myState', {
    url: '/something/{id}',
    templateUrl: '/myTemplate.html',
    controller: function() {},
    resolve: {
        oneThing: function($stateParams) {
            console.log($stateParams); // comes through fine
            var state = this;
            console.log(state); // will give you a "raw" state object
        }
    }
})

The first log will return what you'd expect. The second log will return a "raw" (for lack of a better term) state object. So, for instance, to get the state's name, you can access, this.self.name.

I realize this isn't preferred...it would be a lot nicer if $state (or another standardized object) could provide this information for us at the resolve, but this is the best I could find.

Hope that helps...