Fast Tricks

How to defer route definition in an AngularJS web app

3 Comments

Recently, for a project built over AngularJS, I found myself in front of an unusual requirement: I need to define dinamically the routes of the web application, on the basis of a response from the server.
This post is meant to share the process I followed.

Server response

For the sake of simplicity, I assume to have the following backend service, that when called, responds with a json containing the data about the routes.


$result = array(
	"routes" => array(
		array("name" => "/home", "templateUrl" => "partials/home.html", "controller" => "HomeCtrl", "isFree" => true),
		array("name" => "/contact", "templateUrl" => "partials/contact.html", "controller" => "ContactCtrl", "isFree" => true),
		array("name" => "/about", "templateUrl" => "partials/about.html", "controller" => "AboutCtrl", "isFree" => true)
	),
	"default" => "/home"
);

$json = json_encode($result);
echo $json;

return;

Deferred Routes Definition

Normally routes are defined in the configuration block, because the $routeProvider can be used only there.
Unfortunately when the configuration block is executed all the most important services of AngularJS are still undefined; for this reason it’s not possible to use $http to query the server for the route list.
So in the configuration block I just created a global reference to $routeProvider, which can be used practically everywhere to configure the routes of the web application.


'use strict';

var $routeProviderReference;
var app = angular.module('myApp', ['myApp.controllers']);

app.config(['$routeProvider', function($routeProvider) {
	$routeProviderReference = $routeProvider;
}]);

Now, to get the other routes the first step is to query the server to get the data.
This should be done as soon as possible; and for this reason the best solution is to use the run method to register a callback which is executed when the injector is done loading all modules.


app.run(['$rootScope', '$http', '$route', function($rootScope, $http, $route) {

	$http.get('get-routes.php').success(function (data) {		

		var j = 0,
			currentRoute;

		for ( ; j < data.routes.length; j++ ) {

			currentRoute = data.routes[j];

			$routeProviderReference.when(currentRoute.name, {
				templateUrl: currentRoute.templateUrl,
				controller: currentRoute.controller,
				isFree: currentRoute.isFree
			});

		}

		$routeProviderReference.otherwise({ redirectTo: data.default });

		$route.reload();

	});

}]);

A word on possible applications

Last week I wrote a post about an approach to user authentication in AngularJS web app; and in this occasion I already wrote about how to mantain reserved the reserved content of a web application. Of course deferring the definition of the routes, and differentiating the available routes for a logged user, from those available to a simple visitor, can help to achieve this purpose.

Bruno

I'm Bruno Scopelliti, web developer from Bologna, Italy. I use this space to share my experiences, experiments and thoughts. I'm also on Google+, Twitter, and LinkedIn.

More Posts

Standard
  • Camilo Lopes

    very good! congratulations !!

  • Andrew Lank

    This is really cool! Just a small question: So you would define references, like tags to your controllers inside those templates? And if so you would still need an additional layer of protection in obtaining them right? Unless you define them inline, that is angular code inside the tag instead of referencing (). Just trying to implement something like this and I’m almost there but the restriction to the controller is what is kinda baffling me right now.

  • http://renzocastro.com Renzo Castro Jurado

    I used this with ocLazyLoad and work nice! Thanks!