Angularjs Routing Tutorial using ui-router

UI-router is a routing framework for AngularJS. It’s a flexible alternative to ngRoute as it supports Nested & Multiple Named views. Whereas ngRoute functions based on routes URL, ui-router is based on states in application. With ui-router, your user interface can be organized into states which can have all sorts of logic applied on them as with ngRoute.

This post and demo is inspired by Official UI-router documentation.


Live Demo

Lets see a quick and simple example showing AngularJS routing using ui-router, before diving into details.

Above trivial application contains two links : Business and Portfolio. Business page contains Nested views, while Portfolio page contains multiple named views. We will learn each step in details creating required files along the way.

Basic Setup

Before we continue, we need to make sure to perform some initial setup.

a.Including the AngularJS UI Route Module source code in Application’s HTML

<script src="http://angular-ui.github.io/ui-router/release/angular-ui-router.js">

Very first step is to include the Javascript containing source code of UI routing module. It can be included in head or body section.

b.Include the ui.router module as a dependency of our main AngularJS app module

angular.module('routingDemoApp',['ui.router'])

Next step is to include the ‘ui.router’ module as a dependency to the main module. It is required before we can use anything from this routing module in our code. Notice that it is different than ngRoute where we include ngRoute module

Below is the index.html for our application.

<html ng-app='routingDemoApp'>
	<head>
		<title>UI Router Demo App - Multiple, Nested States & Views</title>
		<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/css/bootstrap.min.css" rel="stylesheet"/>
	</head>
	<body  class="container">
		<h2>AngularJS UI Router Application</h2>

		<nav class="navbar navbar-default row">
			<div class="container-fluid">
				<div class="navbar-header">
					<ul class="nav navbar-nav">
						<li><a ui-sref="business">Business</a></li><!--State Transition on click-->
						<li><a ui-sref="portfolio">Portfolio</a></li><!--State Transition on click-->
					</ul>
				</div>
			</div>
		</nav>
		
		<div class="row">
			<div class="span12">
				<div class="well" ui-view></div><!--Content of the above defined business & portfolio states will be injected here -->        
			</div>
		</div> 		

		<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.4/angular.js"></script>
		<script src="http://angular-ui.github.io/ui-router/release/angular-ui-router.js"></script>
		<script src="app.js"></script>
	</body>
</html>

Our index.html page contains two links Business & Portfolio. Notice that hyperlink for them uses ui-sref instead of regular href. ui-sref is a directive which in addition to auto-generating href attribute, binds a link [<a> tag] to a state. Clicking the link will trigger a state transition with optional parameters. In above excerpt, we have defined which states will be used [business or portfolio] when those links will be clicked. Actual states itself will be defined in config section [which we will cover in app.js in a moment].

Additionally, notice the ui-view directive in above HTML. The ui-view directive tells angularJS where to inject the templates your states refer to.

When a state is activated, its templates are automatically inserted into the ui-view of its parent state’s template. If it’s a top-level state—which ‘business’ is because it has no parent state–then its parent template is index.html.

1) Nested Views

Our Business page business.htmldemonstrates Nested states & views.

<h1>Business page</h1>
<hr/>
<strong>This page shows Nested states & views. Click on below links to see Nested states in action.</strong>
<ul>
	<li><a ui-sref=".products">Show Products</a></li>
	<li><a ui-sref=".services">Show Services</a></li>
</ul>

<div class="panel panel-default" ui-view></div>

This page contains two links Products & Services. Notice that this page have it’s own ui-view as well! This represents Nesting view. Moreover, these links uses ui-sref directive, but notice that the states are named with a dot prefix [.products & .services]. This represents Nested States. We use dot donation when linking nested views. When clicking on these links, templates of these nested views will be injected in ui-view of their parent page which in this case is Business page.

Configure states within config using $stateProvider

Let’s see how to actually define/configure states with ui.router. Below is our app.js file.

var App = angular.module('routingDemoApp',['ui.router']);

App.config(['$stateProvider', '$urlRouterProvider', function($stateProvider, $urlRouterProvider){
				// For any unmatched url, send to /business
				$urlRouterProvider.otherwise("/business")
				
				$stateProvider
						.state('business', {//State demonstrating Nested views
							url: "/business",
							templateUrl: "business.html"
						})
						.state('business.products', {//nested state [products is the nested state of business state]
							url: "/products",
							templateUrl: "products.html",
							controller: function($scope){
								$scope.products = ["Computer", "Printers", "Phones", "Bags"];
							}
						})
						.state('business.services', {//nested state [services is the nested state of business state]
							url: "/services",
							templateUrl: "services.html",
							controller: function($scope){
								$scope.services = ["Selling", "Support", "Delivery", "Reparation"];
							}
						})

						.state('portfolio', {//State demonstrating Multiple,named views
							url: "/portfolio",
							views: {
								"" 	:    { templateUrl: "portfolio.html" },
								"view1@portfolio": { template: "Write whatever you want, it's your virtual company." },
								"view2@portfolio": { templateUrl: "clients.html" ,
									controller: function($scope){
											$scope.clients = ["HP", "IBM", "MicroSoft"];
									}
								}
							}
						})
			}]);

We define our states in the module’s config function , injecting $stateProvider & $urlRouterProvider services. The $urlRouterProvider provides us the way to configure a default URL. The $stateProvider provides us state(name,object) functions in order to define all states in one place. The state function takes two arguments:

  • First argument is the name of state that specifies when this particular state is applicable.
  • The second is a configuration object that specifies what needs to happen when the particular state is encountered/active.

In our app.js file, we have define two main states business & portfolio, and two nested states of business named business.products & business.services. We can say that products and services are child states and there parent state is business [look at dot notation].

When a state is activated [for example by clicking on products or services link in business page], its templates [products.html or services.html] are automatically inserted into the ui-view of its parent state’s template [business page].

Let’s understand it in more detail:

						.state('business', {
							url: "/business",
							templateUrl: "business.html"
						})
						.state('business.products', {
							url: "/products",
							templateUrl: "products.html",
							controller: function($scope){
								$scope.products = ["Computer", "Printers", "Phones", "Bags"];
							}
						})
						.state('business.services', {
							url: "/services",
							templateUrl: "services.html",
							controller: function($scope){
								$scope.services = ["Selling", "Support", "Delivery", "Reparation"];
							}
						})
  • When user clicks on business page link [<a ui-sref=”business”>Business</a> in index.html], business state gets activated, and it’s template [business.html] is inserted into the ui-view of its parent state’s template. Since business state does not have a parent state [no dot in name], it’s parent is index.html.So business.html will be injected into ui-view of index.html.
  • When user clicks on products link [<a ui-sref=”.products”>Products</a> in business.html], products state gets activated, and it’s template [products.html] is inserted into the ui-view of its parent state’s template. Products parent is Business [look at the dot notation in business.products], so products.html will be injected into ui-view of business.html. Note that you can assign a controller to your template, as we did in this example.

Below shown are products.html & services.html

<h3>List of Products</h3>
<ul>
  <li ng-repeat="product in products">{{product}}</li>
</ul>
<h3>List of Services</h3>
<ul>
  <li ng-repeat="srv in services">{{srv}}</li>
</ul>

This concludes our Nested States & Views. A more detailed description of Nested States & Views can be found on ui-router wiki.

In the next section we will cover Multiple named views.

2) Multiple,Named Views

Let’s discuss under-the-hood concepts about multiple-named views. Below shown is business.html page demonstrating multiple-named views
portfolio.html

<h1>Portfolio Page</h1>
<hr/>
<strong>This page shows multiple named views in action.</strong>

<div class="row">
	<div class="col-sm-6">
	  <div class="panel panel-default" ui-view="view1"></div>        
	</div>
	<div class="col-sm-6">
	  <div class="panel panel-default" ui-view="view2"></div>        
	</div>
</div>

This page is different than business.html in the sense that it contains more than one ui-view, and they are also Named this time.This is a setup for multiple, named views. These ui-view will be filled by templates defined in state.

Notice that in our app.js we have defined 4 states but we touched only 3 of them till now. It’s time to explore the 4th state.

						.state('portfolio', {//State demonstrating Multiple,named views
							url: "/portfolio",
							views: {
								"" 	:    { templateUrl: "portfolio.html" },<!-- Relative Name, means main template -->
								"view1@portfolio": { template: "Write whatever you want, it's your virtual company." },
								"view2@portfolio": { templateUrl: "clients.html" ,
									controller: function($scope){
											$scope.clients = ["HP", "IBM", "MicroSoft"];
									}
								}
							}
						})

When setting multiple views you need to use the views property on state. views is an object. Note that if you define a views object, your state’s templateUrl, template and templateProvider will be ignored. That is the reason above state does not contain template/templateUrl property [as was other states]. views contains key:value pairs. Key is view-name [which can be Relative or Absolute] and value is object which in turn contains actual templates to be used, and any other property [like controllers,resolve,etc.].

Notice that key of first item in view is blank [”], means it’s relatively named, means portfolio.html content will be inserted into parent template [index.html] when state portfolio is activated.

Other two items in view object are absolutely named in form of viewname@statename, where viewname is the name used in the view directive and state name is the state’s absolute name, e.g. portfolio. That means the specified template can be inserted into mentioned view [view1 of portfolio state e.g.]. Again, a controller can be applied on the template as we did in above example.

That is all about multiple-named views. A more detailed description of Multiple-Named view can be found on ui-router wiki.

Post Spring 4 MVC+AngularJS Routing Example using UI-Router demonstrates a nice example Integating AngularJS UI-Router with Spring 4 MVC. Check it out.

Download Source Code


References