Go to content
Blog / AngularJS /

Defer routes definition in an AngularJS web app

Recently, for a project built on AngularJS, I found myself in front of an unusual requirement: I needed to define dynamically the routes of the web application, on the basis of a response from the server.

Server response

For the sake of simplicity, I assume to have the following backend service, that when called, sends 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;

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 usethe run method to register a callback that 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, currentRoute;
      var def = data.default;
      for (j = 0; 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: def });

      $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 keep 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.