AngularJS Routing Tutorial using ngRoute

AngularJS Routes enable us to implement multiview SPAs [Single Page Applications]. A multiview application would consist of multiple views [HTML Templates where each template is associated with a specific route] which would be loaded dynamically as a result of user action (clicking a link, typing a specific URL in browser e.g.). Using routing, AngularJS application can show different content based on which route is selected. Routes are basically bookmark-able URL’s to specific content [specific view] of AngularJS application. Let’s get going.

Hashbang URL’s

AngularJS Routing leverages hashbang URLs, means routes are added after # in URL. For example, normally URL looks like http://www.angularapp.com/first/page. In a Single-Page Application, however, it would usually look like http://www.angularapp.com/#/first/page. This is due to the fact that Browser treats URL with # differently than standard URL.

Thus following URL will all make same request to server.

http://angularapp.com/#/first
http://angularapp.com/#/second
http://angularapp.com/#/third

When User clicks on any of these links (or directly types them in the browser) , same request (http://angularapp.com/) is sent to the server. Any URL fragment after the # sign gets ignored by the browser in the server call. It becomes the job of client to deal with part of URL after the #. AngularJS in this case will look at the route [part of URL after the #] and decide which HTML template to load.

Let’s see a quick and simple example showing AngularJS routing, before diving into details.

<html>
	<head>
		<title>RoutingDemo App</title>
	</head>
	<body ng-app='routingDemoApp'>
	
		<h2>AngularJS Routing Application</h2>
		<ul>
			<li><a href="#/">Default Route</a></li>
			<li><a href="#/computers">Computer Route</a></li>
			<li><a href="#/printers">Printers Route</a></li>
			<li><a href="#/blabla">Unknown Route</a></li>
		</ul>
		
		<div ng-view></div>
		<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.4/angular.js"></script>
		<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.4/angular-route.js"></script>
		<script>
			angular.module('routingDemoApp',['ngRoute'])
			.config(['$routeProvider', function($routeProvider){
				$routeProvider
				.when('/',{template:'This is the default Route'})
				.when('/computers',{template:'This is the computers Route'})
				.when('/printers',{template:'This is the printers Route'})
				.otherwise({redirectTo:'/'});
			}]);
		</script>
	
	
	</body>
</html>

Live Demo

Let’s dig deeper, explaining each step in detail.

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

<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.4/angular-route.js"></script>

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

2.Include the ngRoute module as a dependency of our main AngularJS app module

angular.module('routingDemoApp',['ngRoute'])

Next step is to include the ‘ngRoute’ module as a dependency to the main module. It is required before we can use anything from this routing module in our code.

3.Using ngView directive

<div ng-view></div>

Next step is to mark which section of the page AngularJS should change when the route changes. With the ngRoute module, this is done using the ng-view directive in the HTML. Please note that with ngRoute, there can be only One ng-view per application. If your application demands more, you should prefer ui-router over ngRoute.

4.Configure $routeProvider

Define our routes in the config section using the $routeProvider service. Below is the simplest configuration to configure routes using @routeProvider.

			module.config(['$routeProvider', function($routeProvider){
				$routeProvider
					.when('/',{template:'This is the default Route'})
					.when('/computers',{template:'This is the computers Route'})
					.when('/printers',{template:'This is the printers Route'})
					.otherwise({redirectTo:'/'});
			}]);

AngularJS module’s config function is used to configure the routes. config function takes an array as parameter where the last element is the definition function while the others are dependencies to be injected which will eventually be used in definition function. In this case, $routeProvide dependency is injected.

The $routeProvider provides us when(path,object) & otherwise(object) functions in order to define all routes in one place. The when function takes two arguments:

  • First argument is a URL or a URL regex that specifies when this particular route is applicable.
  • The second is a route configuration object that specifies what needs to happen when the particular route is encountered.

In our example, we kept it simple by telling AngularJS to load only a certain HTML template that we specify inline, when that route is encountered. Configuration object provides many configuration options to configure. We will go in details in the next section.

The otherwise function is a catch-all function which specifies what AngularJS needs to do if the user tries to go to a URL that is not specified in the configuration. It takes configuration object as the argument. Most of the time, however, you will see that otherwise function takes redirectTo as a key which will simply redirect any unknown URL to the route mentioned in value of redirectTo key.

Route Configuration Object in detail

Previous example was very simple where we simply loaded different templates for different routes, and nothing else. The AngularJS route definition allows us to define more complex templates. The $routeProvider.when function takes a URL or URL regular expression as the first argument, and the route configuration object as the second.

The syntax with route configuration object is as follows:

$routeProvider.when(url, {
	template: string,
	templateUrl: string,
	controller: string, function or array,
	controllerAs: string,
	redirectTo: string, function,
	resolve: object<key, function>
});

Each key is explained below:

template: When the HTML to be displayed is not very large, we can inline it as a string, using ‘template’ as a key and this string as a value in configuration object. AngularJS directly inserts this template HTML into the ng-view directive.

.when('/computers',{template:'This is the computers Route'})

This loads the content specified with template key into the ng-view.

templateUrl: When the HTML to be displayed is complex and large, it’s better to break them into separate templates/files, and give the URL to the HTML file as the templateUrl. AngularJS loads the HTML file from the server when it needs to display the particular route.

$routeProvider.when('/computers', {
	templateUrl: 'views/computers.html',
});

This fetches views/computers.html from the server and loads it into the ng-view.

controller: Using controller key, we can define the controller to be used for a particular route. There are two ways we can define the controller to be used for a particular route.

  • In case the controller has already been defined using module.controller() function in the application, we can just specify the name of controller, and that controller code would be accessible from within route template.
    //earlier in application
    App app = angular.module('myApp',['ngRoute'])
    app.controller('AppController1', [function() {
              var self = this;
              self.message= 'Hello';
              self.doSomething = function() {
                  self.message = 'Bye';
              };
          }]);
    
    
    app.config(['$routeProvider', function($routeProvider){
    				$routeProvider
    					.when('/',{template:'This is the default Route'})
    					.when('/computers',{template:'This is the computers Route. And the message is {{ctrl.message}}', 
                                                        controller:'AppController1 as ctrl'})
    					.when('/printers',{template:'This is the printers Route'})
    					.otherwise({redirectTo:'/'});
    			}]);
    
    
    
  • Other case is to define the controller inline, right along with route.
    $routeProvider.when('/computers', {
           template: 'This is the computers Route',
           controller: ['$log', function($log) {
                  $log.log('Computers route has been loaded!');
           }]
    });
    

    In above example, we have defined the controller definition inline with the route. One drawback of this approach is that we can not reuse the template if we wanted to, without duplicating the code.

controllerAs: You can use controllerAs syntax in case you don’t want to name the controller using ‘as’ syntax with ‘controller’ key [as we did in previous example. The two route definitions in the following example are equivalent in terms of functionality:


				$routeProvider
				.when('/computers',{template:'This is the computers Route. And the message is {{ctrl.message}}', 
                                                    controller:'AppController1 as ctrl'})

				$routeProvider
				.when('/',{template:'This is the default Route'})
				.when('/computers',{template:'This is the computers Route. And the message is {{ctrl.message}}', 
                                                    controller:'AppController1'
                                                    controllerAs:'ctrl'

It’s your personal preference which one you should choose.

redirectTo: There might be cases where you want to redirect to a different URL [For example, some URL has been moved,or does not exist anymore]. In those cases, ‘redirectTo’ key comes handy. It is specially useful in error handling.

$routeProvider.when('/computers', {
	template: 'This is the computers Route.'
});
$routeProvider.when('/desktops', {
	redirectTo: '/computers'
});

In above example, AngularJS will open /#/computers when user enters either /#/computers or /#/desktops in the browser.

resolve: Resolves provides us a way to define a set of asynchronous tasks to execute before the route is loaded. In case any of the asynchronous tasks is not successful, route will not be loaded. It is a handy way to check for example if a user is logged in and has authorization before accessing a protected route. A resolve is a set of keys and functions. Each function can return a value or a promise.

Let’s see this using more realistic setup:

$routeProvider
		.when('/items/computers', {
			templateUrl: 'items/computers',
			controller : "ItemListController as itemListCtrl",
			resolve: {
				  async: ['ItemService', function(ItemService) {
				       return ItemService.fetchAllItems('computers');
               	                  }]
                        }
		})

App.factory('ItemService', ['$http', '$q', function($http, $q){

	return {
		
			fetchAllItems: function(category) {
					return $http.get('http://localhost:8080/Spring4MVCAngularJSRoutingExample/item/'+category)
							.then(
									function(response){
										return response.data;
									}, 
									function(errResponse){
										console.error('Error while fetching Items');
										return $q.reject(errResponse);
									}
							);
			}
	};

}]);

App.controller('ItemListController', ['async', function(async) {
          var self = this;
          self.items=async;
}]);

In above example, when user tries to access /item/computers, resolves comes in action. It has a set of key [it has named ‘async’ here but you can name anything you want] and function. There can be more than one key:function in one resolve definition. function in this example takes a user defined service ‘ItemService’ as a dependency and calls fetchAllItems function which send a request to server. In this particular scenario, resolve returns a promise, which is passed in controller here. In case the server returns unsuccessful response, route will not be loaded. In case the server response in success [list of items], route gets loaded.

Access route params using $routeParams service

Routes can have parameters. These parameters can then directly be accessed in controllers using AngularJS #routeParams service. Let’s take an example:

<html>
    <head>
        <title>RoutingDemo App</title>
    </head>
    <body ng-app='routingDemoApp'>
     
        <h2>AngularJS Routing Application</h2>
        <ul>
            <li><a href="#/">Default Route</a></li>
            <li><a href="#/route/1?name=crap&age=33">route with params and query string</a></li>
            <li><a href="#/blabla">Unknown Route</a></li>
        </ul>
         
        <div ng-view></div>
        <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.4/angular.js"></script>
        <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.4/angular-route.js"></script>
        <script>
            angular.module('routingDemoApp',['ngRoute'])
            .config(['$routeProvider', function($routeProvider){
                $routeProvider
                .when('/',{template:'This is the default Route'})
                .when('/route/:id',
					{
						template:'Referred item is {{ctrl.id}}' + ' and passed query-string values are {{ctrl.qStrName}} , {{ctrl.qStrAge}}',
						controller: ['$routeParams', function($routeParams) {
							var self=this;
							self.id = $routeParams.id;
							self.qStrName = $routeParams.name;
							self.qStrAge = $routeParams.age;
						}],
						controllerAs: 'ctrl'
					})
                .otherwise({redirectTo:'/'});
            }]);
        </script>
    </body>
</html>

Live Demo

In above example, route is defined as /route/:id. Look at : followed by a variable. This is an indication for AngularJS routing that there will be a value after the /route in the URL that should be stored and further make available in controller using $routeParams service.

For example, URL’s like /route/12345?a=2&b=3 will match the route /route with id 12345 and query string variables a & b. Now those values can be accessed in controller code using $routeParams service. Any parameter [preceded by ‘:’] in route can be accessed in controller by it’s name using $routeParams.paramName. Additionally, any query string passed in URL can be accessed in controller using $routeParams.variableName

That’s it. Complete source code for this post can be downloaded from Spring 4 MVC+AngularJS Routing Example using ngRoute.