AngularUI Router is an amazing tool.

If you have read my article on resource resolution, you already know how to make sure that promises are resolved before controllers are instantiated. In fact, if you haven't read the article yet, I strongly recommend that your read it first before reading this article.

The challenge

Resolving promises before controllers are instantiated ensures that critical data is available in your application at the time you need it. In many cases information such as configuration data or translation dictionaries is required to be present at all times in your application.

You could hard code the information in your AngularJS code, but what if you need to load the information from your server?

And what if you need that information in every state?

The normal approach

Armed with the trick from my article on resource resolution, we can easily solve the problem like this:

// Use $stateProvider to configure your states. $stateProvider .state('home', { url: '/home', resolve: { // Get AngularJS resource to query Config: 'Config', // Use the resource to fetch data from the server config: function(Config){ return Config.get().$promise; }, // Extract the configuration data // that we need for the home state data: function(config){ return config.homeConfigData; } }, views: { 'content': { templateUrl: '/home.html', // The config data is guaranteed to be resolved // when the HomeCtrl is instantiated controller: 'HomeCtrl' } }) .state('articles', { url: '/articles', resolve: { // Get AngularJS resource to query Config: 'Config', // Use the resource to fetch data from the server config: function(Config){ return Config.get().$promise; }, // Extract the configuration data // that we need for the articles state data: function(config){ return config.articlesConfigData; } }, views: { 'content': { templateUrl: '/articles.html', // The config data is guaranteed to be resolved // when the HomeCtrl is instantiated controller: 'ArticlesCtrl' } });

This works like a charm, but the code to fetch the configuration becomes repetitive.

If your application has many states that require the same information, this would rapidly become hard to maintain.

Centralize resolution logic

Ui-router has this extremely handy feature where child states inherit resolves from their parent states. From the ui-router docs:

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.

So to avoid code duplication, let's introduce an abstract root state and call it main :

// Use $stateProvider to configure your states. $stateProvider .state('main', { url: '/main', // Make this state abstract so it can never be // loaded directly abstract: true, // Centralize the config resolution resolve: { // Get AngularJS resource to query Config: 'Config', // Use the resource to fetch data from the server config: function(Config){ return Config.get().$promise; } }) // The home state now becomes a child state of main // so it can access the resolves of main .state('main.home', { url: '/home', resolve: { // Inject the config resolved in the main state // and extract the data we need data: function(config){ return config.homeConfigData; } }, views: { '[email protected]': { templateUrl: '/home.html', // The config data is guaranteed to be resolved // when the HomeCtrl is instantiated controller: 'HomeCtrl' } }) // The articles state also becomes a child state of main // so it can access the resolves of main .state('main.articles', { url: '/articles', resolve: { // Inject the config resolved in the main state // and extract the data we need data: function(config){ data = config.articlesConfigData; } }, views: { '[email protected]': { templateUrl: '/articles.html', // The config data is guaranteed to be resolved // when the HomeCtrl is instantiated controller: 'ArticlesCtrl' } });

All shared code has now been centralized in the main parent state. The state is abstract , because we don't want visitors to be able to directly navigate to the state. We just want to use the state to perform resource resolution for its child states.

Update 2016-01-04: View a working example here.

Ensure resolution in child state

Notice that the config from the main state is injected as an argument in the resolve from the child state.

Since the config in the main state returns a promise, it is guaranteed to be resolved by the time the controller of the child state is instantiated.

It is important to note that this is only guaranteed when the config in the parent state returns a promise.

If the parent state would have looked like this:

// Use $stateProvider to configure your states. $stateProvider .state('main', { url: '/main', // Make this state abstract so it can never be // loaded directly abstract: true, // Centralize the config resolution resolve: { // Get AngularJS resource to query Config: 'Config', // Use the resource to fetch data from the server // but don't return the promise config: function(Config){ return Config.get(); } });

then you can rest assured that config will eventually be resolved, but this time it is not guaranteed to be resolved by the time the child state controller is instantiated.

Resolve like a pro

With this powerful technique you can:

create hierarchies of promise based dependencies

centralize logic to resolve application-wide dependencies

specify which resolves are required to be resolved in each state individually

Taking control over dependency resolution allows you to take your application to a whole new level by letting you control exactly what data is available at the time you need it.

Centrally. Hierarchically. Per state. Using promises. Like a pro.