This tutorial is out of date and no longer maintained.
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.
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.
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.
Let’s do something similar to the other routing tutorial we made.
View Demonstration of Angular routing and templating on plnkr
Let’s create a Home and About page.
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.
<!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
.
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 the information.
<div class="jumbotron text-center">
<h1>The Home 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.
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.
<div class="jumbotron text-center">
<h1>The Home 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 it into our new <div ui-view></div>
.
In our app.js
file, let’s create those nested states.
...
$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: 'Random String Data'
})
...
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
.
The 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.
<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.
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.
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.
<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
.
...
.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: '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
}
];
});
...
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.
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 are columnOne@about
and columnTwo@about
.
Having the naming scheme this way lets 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.
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.
Thanks for learning with the DigitalOcean Community. Check out our offerings for compute, storage, networking, and managed databases.
This textbox defaults to using Markdown to format your answer.
You can type !ref in this text area to quickly search our full set of tutorials, documentation & marketplace offerings and insert the link!