Join us in our way to learning Angular.js

Setting Up Individual Views With Ui-router and Ui-router-extras

I had a problem: My application had multiple independent parts, which needed their own states. For example, I have a toolbar that’s on top and a sidebar on the right. The user can change each of the parts without affecting the other, and setting it up as a normal ui-router state tree will not work.

The standard ui-router has no concept of parallel states. Everything must be modeled as a tree, which means a setup like this doesn’t work. For example, changing the sidebar’s state would affect the toolbar’s state as well – which is not something we want.

Thankfully there’s ui-router-extras, which adds support for so-called “sticky states” or “parallel states”. We can use this to have as many individual parts, that have their own parallel state trees, as we want.

Let’s go through a small sample app and look how to set this up step by step. You can find the full sample app here so you can follow along more easily.

Setting up the necessary libraries

ui-router-extras recommends ui-router version 0.2.8 or newer. As for ui-router-extras itself, we can either install it completely, or just install the core and sticky modules which are needed for sticky states.

An easy way to set it up is to use cdnjs and simply include the necessary scripts like so:

Next, we’ll set up the $stateProvider. Both the toolbar and sidebar states are set up in the same way, the main difference being which templates and ui-view used.

123456789101112131415

app.config(['$stateProvider',function($stateProvider){//set up the toolbar parent state, and its two child-states$stateProvider.state('toolbar',{sticky:true,views:{toolbar:{template:'<div ui-view></div>'}}}).state('toolbar.state1',{templateUrl:'toolbar-state1.html'}).state('toolbar.state2',{templateUrl:'toolbar-state2.html'});}]);

Here we set up three states that we need for the toolbar. First, we set up the base state toolbar. We set sticky: true to make it a sticky states. Its template is just a div, containing a ui-view. It’s important to set it up like this – if you have multiple states accessing the same named view, even if they’re the children of the sticky state, it will not work correctly.

You can put other content into the base state’s template if you want, or set it up using templateUrl – just make sure you include a ui-view within the template for it. Otherwise this won’t work correctly.

Each of the child states – toolbar.state1 and toolbar.state2 – have a templateUrl. The template contents are placed within the ui-view from the parent toolbar state. If you want, you can include a state controller in addition to a template, or any other state properties.

Next, we’ll set up the sidebar states. These work exactly the same way as the toolbar states – we have a base sidebar state with a ui-view, and two child states.

12345678910111213

//set up the sidebar's states, which are structured the same way$stateProvider.state('sidebar',{sticky:true,views:{sidebar:{template:'<div ui-view></div>'}}}).state('sidebar.state1',{templateUrl:'sidebar-state1.html'}).state('sidebar.state2',{templateUrl:'sidebar-state2.html'});

Note the only differences in the sidebar states are the state names, templates and the target ui-view.

Setting up the button controller

In a real application, you probably wouldn’t want to add $state directly into scope as here, but this works for demonstration purposes.

Loading default states

Depending on how your application and views are set up, you may want to load a default state for each of your individual views.

Normally you could use the url property on a state to choose which to load, but if you want to load multiple defaults – for example, if we want to load a state into both the sidebar and the toolbar – then it won’t work, as ui-router requires each state to have a unique URL.

We can work around it by manually changing the state in the application’s run-block. However, simply calling $state.go twice in a row will not work. We need to chain it using the promise like so:

Conclusion

Although ui-router is a bit limited when it comes to parallel states like this, it’s mostly fixed by ui-router-extras and workarounds like chaining the default state loading. One of ui-router’s 1.0 version goals is better support for scenarios like this, but until then, this is the best way to do it.