Technology

UI Router – Out Of Box Routing for AngularJS

Krishnal Jadav

What is routing?

URL routing is a popular approach to match the contents of a URL to specific functionality within a web application. URL routes programmatically present specific content to users based on the URL that they are visiting. It is a popular approach that has proven to be very effective.

Existing ngRoute

ngRoute has a straight forward implementation, map the URL to specific JavaScript code that is executed when the URL changes. All of this to preserve the back button functionality.

Limitations In ngRoute

The primary lack with the ngRoute implementation is that, it ultimately focuses on the URL! The URL is the dominant force which determines where you are in the application. This is the reason why you can only have a single ng-view directive on a page at a time.

The handicap here is forcing you to build all of the potentially reusable components into a single view and then using directives such as ng-include as a workaround when you need to nest your views. Things can get even hairier when you start having ng-hide and ng-show directives everywhere.

UI Router

UI-Router, largely influenced by frameworks such as Durandal.js and Ember.js, pushed the routing experience within Angular to a whole new level.

  • States

States are the driving force in UI-Router. The $stateProvider is similar to ngRoute, except the state is focused on the location in the application and not the URL.

States can be centrally defined in your application module .config() or a better solution would be to define the states on individual modules.

We will see the installation first and then simple route example

  • Installation

To install the ui-router library, we can either download the release or use Bower:

$ bower install angular-ui-router –save

We need to link the library to our view:

[sourcecode language=”html”]
<script type=”text/javascript” src=”app/bower_components/angular-ui-router/release/angular-ui-router.js”></script>
[/sourcecode]

 

And we need to inject the ui.router as a dependency in our app:

[sourcecode language=”javascript”]
angular.module(‘myApp’, [‘ui.router’])
[/sourcecode]

 

Now, unlike the built-in ngRoute service, the ui-router can nest views, as it works based off states, not just a URL.

Instead of using the ng-view directive as we do with the ngRoute service, we’ll use the ui-view directive with ngRoute.

When dealing with routes and states inside of ui-router, we’re mainly concerned with which state the application is in as well as at which route the web app currently stands.

[sourcecode language=”html”]
<div ng-controller=”DemoController”>
<div ui-view></div>
</div>
[/sourcecode]

  • Simple Routing Example

[sourcecode language=”javascript”]
angular.module(myApp)
.config(function($stateProvider, $urlRouterProvider) {
$stateProvider
.state(‘start’, {
url: ‘/start’,
templateUrl: ‘partials/start.html’
})
});
[/sourcecode]

  • Nested routing

We can use the url parameter to append routes in order to provide for nested routes. Using it in this way enables us to support having multiple ui-views inside our page and our templates. For instance, we can nest individual routes inside of our /inbox route above.

[sourcecode language=”javascript”]
$stateProvider
.state(‘inbox’, {
url: ‘/inbox/:inboxId’,
template: ‘<h1>Welcome to your inbox</h1>
<a ui-sref=”inbox.priority”>Show priority</a>
<div ui-view></div>
</div>’,
controller: function($scope, $stateParams) {
$scope.inboxId = $stateParams.inboxId;
}

})
.state(‘inbox.priority’, {
url: ‘/priority’,
template: ‘<h2>Your priority inbox</h2>’
});
[/sourcecode]

Our first route will match as we expect from above. We now also have a second route, a child route that matches under the inbox route: Our syntax (.) indicates that it is a child.

/inbox/1 matches the first state, and /inbox/1/priority matches the second state. With this syntax, we can support a nested URL inside of the parent route. The ui-view inside of the parent view will resolve to the priority inbox.

  • Params

The params option is an array of parameter names or regexes. This option cannot be combined with the url option. When the state becomes active, the app will populate the $stateParams service with these parameters.

  • Views

We can set multiple named views inside of a state. This feature is a particularly powerful one in ui-router: Inside of a single view, we can define multiple views that we can reference inside of a single template.

If we set the views parameter, then the state’s templateUrl, template, and templateProvider will be ignored. If we want to include a parent template in our routes, we’ll need to create an abstract template the contains a template.

Let’s say we have a view that looks like:

[sourcecode language=”html”]
<div>
<div ui-view=”filters”></div>
<div ui-view=”mailbox”></div>
<div ui-view=”priority”></div>
</div>
[/sourcecode]

We can now create named views that fill each of these individual templates. Each of the subviews can contain their own templates, controllers, and resolve data.

[sourcecode language=”javascript”]
$stateProvider
    .state(‘inbox’, {
        views: {
            ‘filters’: {
                template: ‘<h4>Filter inbox</h4>’,
                controller: function($scope) {}
            },
            ‘mailbox’: {
                templateUrl: ‘partials/mailbox.html’

            },
            ‘priority’: {
                template: ‘<h4>Priority inbox</h4>’,
                resolve: {
                    ‘facebook’: function() {
                        return FB.messages();

                    }
                }
            }
        }
});
[/sourcecode]

In this example, we have two named views embedded inside an abstract view.

  • Multiple Named Views

Reusable, modular and maintainable code should be your goal when designing modern web applications. UI-Router enables you to separate your states into multiple named views which you can inject with specific state names.

For instance, suppose you have a standard view page with a filters bar, tabledata graph, a pie chart and maybe other sub-views such as the header and footer. The center page may change based on user interaction, do you want to navigate to a whole new view and re-render the navbar and sidebar? No, you want to reuse those components and the logic behind the scenes.

Image1

 

  • HTML markup to locate your named views:

[sourcecode language=”html”]
<div ui-view=”filters”></div>
<div ui-view=”tabledata”></div>
<div ui-view=”graph”></div>
[/sourcecode]

State configuration for your named views:

[sourcecode language=”javascript”]
$stateProvider
    .state(‘report’,{
        views: {
            ‘filters@’: { },
            ‘tabledata@’: { },
            ‘graph@’: { }
        }
});
[/sourcecode]

UI-Router gives us the power to have parallel views that can reference previous states within deeply nested states.

  • Abstract States

We can never directly activate an abstract template, but we can set up descendants to activate.

Abstract templates can provide a template wrapper around multiple named views, or they can pass $scope objects to descendant children. We can use them to pass around resolved dependencies or custom data or simply to nest several routes under the same ‘url’ (e.g., have all routes under the /admin URL).

Setting up an abstract template is just like setting up a regular state, except that we’ll set the abstract property:

[sourcecode language=”javascript”]
$stateProvider
    .state(‘admin’, {
        abstract: true,
        url: ‘/admin’,
        template: ‘<div ui-view></div>’
    })
    .state(‘admin.index’, {
        url: ‘/index’,
        template: ‘<h3>Admin index</h3>’
    })
    .state(‘admin.users’, {
        url: ‘/users’,
        template: ‘<ul>…</ul>’
});
[/sourcecode]

Reference and Resources:

Take a look at the following links for deep documentation behind UI-Router!

 

Author

Krishnal Jadav

Technology seeker, dreamer and a full stack developer working on progressive technologies. Always try to achieve goals with best efforts. Very passionate about AngularJS and NodeJS.

Want more stories like this?

Subscribe to receive our exclusive insights.
  • This field is for validation purposes and should be left unchanged.