angular-routing-ui-router
Javascript

AngularJS Routing Using UI-Router

AngularJS provides a great way to make single page applications. When creating single page applications, routing will be very important. We want our navigation to feel like a normal site and still not have our site refresh. We’ve already gone through Angular routing using the normal ngRoute method.

Today we’ll be looking at routing using UI-Router.

Overview

What is AngularUI Router?

The UI-Router is a routing framework for AngularJS built by the AngularUI team. It provides a different approach than ngRoute in that it changes your application views based on state of the application and not just the route URL.

States vs URL Route

With this approach, your views and routes aren’t tied down to the site URL. This way, you can change the parts of your site using your routing even if the URL does not change.

When using ngRoute, you’d have to use ngInclude or other methods and this could get confusing. Now that all of your states, routing, and views are handled in your one .config(), this would help when using a top-down view of your application.

Sample Application

Let’s do something similar to the other routing tutorial we made. Let’s create a Home and About page.

Setup

Let’s get our application started. We will need a few files:


- index.html                    // will hold the main template for our app
- app.js                        // our angular code
- partial-about.html            // about page code
- partial-home.html             // home page code
- partial-home-list.html        // injected into the home page
- table-data.html               // re-usable table that we can place anywhere

With our application structure figured out, let’s fill out some files.

<!-- index.html -->

<!DOCTYPE html>
<html>
<head>

    <!-- CSS (load bootstrap) -->
    <link rel="stylesheet" href="//netdna.bootstrapcdn.com/bootstrap/3.1.1/css/bootstrap.min.css">
    <style>
        .navbar { border-radius:0; }
    </style>

    <!-- JS (load angular, ui-router, and our custom js file) -->
    <script src="http://code.angularjs.org/1.2.13/angular.js"></script>
    <script src="//cdnjs.cloudflare.com/ajax/libs/angular-ui-router/0.2.8/angular-ui-router.min.js"></script>
    <script src="app.js"></script>
</head>

<!-- apply our angular app to our site -->
<body ng-app="routerApp">

<!-- NAVIGATION -->
<nav class="navbar navbar-inverse" role="navigation">
    <div class="navbar-header">
        <a class="navbar-brand" ui-sref="#">AngularUI Router</a>
    </div>
    <ul class="nav navbar-nav">
        <li><a ui-sref="home">Home</a></li>
        <li><a ui-sref="about">About</a></li>
    </ul>
</nav>

<!-- MAIN CONTENT -->
<div class="container">

    <!-- THIS IS WHERE WE WILL INJECT OUR CONTENT ============================== -->
    <div ui-view></div>

</div>

</body>
</html>

There’s our HTML file. We will use Bootstrap to help with our styling. Notice that we also load up ui-router in addition to loading Angular. UI Router is separate from the Angular core, just like ngRoute is separate.

When creating a link with UI-Router, you will use ui-sref. The href will be generated from this and you want this to point to a certain state of your application. These are created in your app.js.

We also use <div ui-view></div> instead of ngRoute’s <div ng-view></div>.

Let’s start up our Angular application now in app.js.

// app.js
var routerApp = angular.module('routerApp', ['ui.router']);

routerApp.config(function($stateProvider, $urlRouterProvider) {
    
    $urlRouterProvider.otherwise('/home');
    
    $stateProvider
        
        // HOME STATES AND NESTED VIEWS ========================================
        .state('home', {
            url: '/home',
            templateUrl: 'partial-home.html'
        })
        
        // ABOUT PAGE AND MULTIPLE NAMED VIEWS =================================
        .state('about', {
            // we'll get to this in a bit       
        });
        
});

Now we have created the routerApp that we already applied to our body in the index.html file.

Here we have a .state() for home and for about. In home, we are using the template file partial-home.html.

Let’s fill out our partial-home.html page so we can actually see information.

<!-- partial-home.html -->

<div class="jumbotron text-center">
    <h1>The Homey Page</h1>
    <p>This page demonstrates <span class="text-danger">nested</span> views.</p>    
</div>


Now we have our site! It doesn’t do much, but we have it.

angular-ui-router-home-first With the boring normal stuff out of the way, let’s get to see why UI-Router has some pretty cool features.

Nested Views Home Page

Let’s look at how we can nest views. We’ll add two buttons to our home page and from there, we will want to show off different information based on what is clicked.

We’re going to add our buttons to partial-home.html and then go into our Angular file and see how we can change it to add nested views.

<!-- partial-home.html -->

<div class="jumbotron text-center">
    <h1>The Homey Page</h1>
    <p>This page demonstrates <span class="text-danger">nested</span> views.</p>  

    <a ui-sref=".list" class="btn btn-primary">List</a>
    <a ui-sref=".paragraph" class="btn btn-danger">Paragraph</a>
</div>

<div ui-view></div>


When linking to a nested view, we are going to use dot denotation: ui-sref=".list" and ui-sref=".paragraph". These will be defined in our Angular file and once we set it up there, we will inject into our new <div ui-view></div>.

In our app.js file, let’s create those nested states.

// app.js
...

$stateProvider

    // HOME STATES AND NESTED VIEWS ========================================
    .state('home', {
        url: '/home',
        templateUrl: 'partial-home.html'
    })

    // nested list with custom controller
    .state('home.list', {
        url: '/list',
        templateUrl: 'partial-home-list.html',
        controller: function($scope) {
            $scope.dogs = ['Bernese', 'Husky', 'Goldendoodle'];
        }
    })

    // nested list with just some random string data
    .state('home.paragraph', {
        url: '/paragraph',
        template: 'I could sure use a drink right now.'
    })

...

Now the ui-sref we defined in home.html are linked to an actual state. With home.list and home.paragraph created, those links will now take the template provided and inject it into ui-view.

Last thing we need to do for the home page is define the partial-home-list.html file. We have also passed in a controller with a list of dogs that we will use in the template file.

<!-- partial-home-list.html -->

<ul>
    <li ng-repeat="dog in dogs">{{ dog }}</li>
</ul>


Now when we click List, it will inject our list of dogs into the template. Or if we click Paragraph, it will inject the string we gave.

angular-routing-ui-router-home-list You can see how easy it is to change different parts of our application based on the state. We didn’t have to do any sort of work with ngInclude, ngShow, ngHide, or ngIf. This keeps our view files cleaner since all the work is in our app.js.

Let’s move on and see how we can have multiple views at once.

Multiple Views About Page

Having multiple views in your application can be very powerful. Maybe you have a sidebar on your site that has things like Popular Posts, Recent Posts, Users, or whatever. These can all be separated out and injected into our template. Each will have its own controller and template file so our app stays clean.

Having our application modular like this also lets us reuse data in different templates.

For our About page, let’s make two columns and have each have its own data. We will handle the view first and then look at how we can do this using UI-Router.

<!-- partial-about.html -->

<div class="jumbotron text-center">
    <h1>The About Page</h1>
    <p>This page demonstrates <span class="text-danger">multiple</span> and <span class="text-danger">named</span> views.</p>
</div>

<div class="row">

    <!-- COLUMN ONE NAMED VIEW -->
    <div class="col-sm-6">
        <div ui-view="columnOne"></div>
    </div>
    
    <!-- COLUMN TWO NAMED VIEW -->
    <div class="col-sm-6">
        <div ui-view="columnTwo"></div>
    </div>

</div>    


There we have multiple views. One is named columnOne and the other is columnTwo.

Why would somebody use this approach? That’s a good question. Are we creating an application that is too modularized and that could get confusing? Taken from the official UI-Router docs, here is a solid example of why you would have multiple named views. In their example, they show off different parts of an application. Each part has its own data, so having each with its own controllers and template files makes building something like this easy.

Now that our view is all created, let’s look at how we can apply template files and controllers to each view. We’ll go back to our app.js.

// app.js

...

    .state('about', {
        url: '/about',
        views: {

            // the main template will be placed here (relatively named)
            '': { templateUrl: 'partial-about.html' },

            // the child views will be defined here (absolutely named)
            'columnOne@about': { template: 'Look I am a column!' },

            // for column two, we'll define a separate controller 
            'columnTwo@about': { 
                templateUrl: 'table-data.html',
                controller: 'scotchController'
            }
        }
        
    });

}); // closes $routerApp.config()


// let's define the scotch controller that we call up in the about state
routerApp.controller('scotchController', function($scope) {
    
    $scope.message = 'test';
   
    $scope.scotches = [
        {
            name: 'Macallan 12',
            price: 50
        },
        {
            name: 'Chivas Regal Royal Salute',
            price: 10000
        },
        {
            name: 'Glenfiddich 1937',
            price: 20000
        }
    ];
    
});

...


angular-routing-ui-router-about Just like that, our About page is ready to go. Now it may be confusing how we nested everything in the views for the about state. Why not define a templateUrl for the main page and then define the columns in a nested view object? The reason for this gives us a really great tool.

Relative vs Absolute Naming

UI-Router assigns every view to an absolute name. The structure for this is viewName@stateName. Since our main ui-view inside our about state, we gave it a blank name. The other two views because columnOne@about and columnTwo@about.

Having the naming scheme this way let’s us define multiple views inside a single state. The docs explain this concept very well and I’d encourage taking a look at their examples. Extremely powerful tools there.

Conclusion

This is an overview of the great tool that is UI-Router. The things you can do with it are incredible and when you look at your application as states instead of going the ngRoute option, Angular applications can easily be created to be modular and extensible.

Chris Sevilleja

Design, development, and anything in between that I find interesting.

View My Articles

Stay Connected With Us
hover these for magic

Get valuable tips, articles, and resources straight to your inbox. Every Tuesday.

Onward (more articles)