dalu dalu - 2 months ago 25x
AngularJS Question

angular-ui-router: resolve on root state

As far as I understand ui.router

You have $stateProvider

in it you can write $stateProvider.state()

you can write

.state('account', {
url: '/account',
template: '<ui-view/>'
.state('account.register', {
url: '/register',
templateUrl: '/account/views/register.html',
data: { title: 'Create Account' },
controller: 'AccountRegister'

but 'account' is a child of some kind of root state.

So my thought is, the root state of ui.router has a
, just like
has the template
, so index.html would be the root state. Even
url: '/'
would be or rather is a child of this root state.

and if I'm able to access the root state and say it should resolve a User service this resolved value should be available on all states (or better on every state that is a child of the root state, aka all). The User service promise makes a
and it returns
{"user":{id: "userid", role: "role"}} or {"user":null}

This would guarantee that the value returned by the User service is always populated.

How do I access the root state and say it should resolve e.g.

Or let me rephrase that.

A user object should be available to all controllers.
The information about the currently logged in user is supplied by a $http.get('/api/user/status')
That's the problem and I'm looking for a DRY solution that, assuming the api is serving, is 100% safe to assume a user object is set, aka the promise is always fulfilled, aka "waiting for the promise to resolve before continuing".


Disclaimer — messing around with you root scope is not a good idea. If you're reading this, please note that the OP, @dalu, wound up canning this route and solved his issue with http interceptors. Still — it was pretty fun answering this question, so you might enjoy experimenting with this yourself:

It might be a little late, but here goes

As mentioned in the comments and from my experience so far, this is not possible to do with the ui-router's api (v0.2.13) seeing as they already declare the true root state, named "". Looks like there's been a pull request in for the past couple years about what you're looking for, but it doesn't look like it's going anywhere.

The root state's properties are as follows:

    abstract: true,
    name: "",
    url: "^",
    views: null

That said, if you want to extend the router to add this functionality, you can do this pretty easily by decorating the $stateProvider:

$provide.decorator('$state', function($delegate) {
  $delegate.current.resolve = {
    user: ['User', '$stateParams', httpRequest]
  return $delegate

Note that there are two currents: $delegate.current - the raw "" state - and $delegate.$current - its wrapper.

I've found a bit of a snag, though, before this becomes the solution you were looking for. Every time you navigate to a new state, you'll make another request which has to be resolved before moving forward. This means that this solution isn't too much better than event handling on $stateChangeStart, or making some "top" state. I can think of three work-arounds off the top of my head:

  • First, cache your http call. Except I can see how this pretty much invalidates certain use-cases, perhaps you're doing something with sessions.
  • Next, use one of your singleton options (controller/service) to conditionally make the call (maybe on just set a flag for first load). Since the state is being torn down, it's controller might be as well - a service might be your only option.
  • Lastly, look into some other way of routing - I haven't used ui.router-extras too much, but sticky states or deep state redirect might do the trick.

I guess, lastly, I'm obligated to remind you to be careful with the fact that that you're working on the root-level. So, i mean, be about as careful as you should be when doing anything in root-level.

I hope this answers your question!