How to defer routes definition in Angular.js?

stej picture stej · Oct 31, 2012 · Viewed 11.1k times · Source

I have configured some basic routes that are available for all users before they log in:

App.config(function ($routeProvider) {
    $routeProvider.
        when('/login', { templateUrl: 'views/login.html', controller: PageStartCtrl.Controller }).
        otherwise({ redirectTo: '/login' });
});

So the only thing user can do is to log in. After the user logs in, I would like to register additional routes like this:

$http
  .post('api/Users/Login', {   User: userName, Password: userPassword })
  .success(function (response : any) {
    App.config(function ($routeProvider) {
      $routeProvider
        .when('/dashboard', 
              { templateUrl: 'part/dashboard.html', 
              controller: DashboardCtrl.Controller });
  });

However, I suppose I should call .config method only once, because the $routeProvider is brand new instance that knows nothing about /login route. Further debugging showed me that the first instance of $resourceProvider is used when resolving view change.

Q: Is there a way how to register routes later?

Solution from Add routes and templates dynamically to $routeProvider might work, but is quite ugly (involved global variable nastyGlobalReferenceToRouteProvider).

Answer

pkozlowski.opensource picture pkozlowski.opensource · Nov 1, 2012

Since routes are defined on a provider level, normally new routes can only be defined in the configuration block. The trouble is that in the configuration block all the vital services are still undefined (most notably $http). So, on the surface it looks like w can't define routes dynamically.

Now, it turns out that in practice it is quite easy to add / remove routes at any point of the application life-cycle! Looking at the $route source code we can see that all the routes definition are simply kept in the $route.routes hash which can be modified at any point in time like so (simplified example):

myApp.controller('MyCtrl', function($scope, $route) {    
    $scope.defineRoute = function() {
        $route.routes['/dynamic'] = {templateUrl: 'dynamic.tpl.html'};
    };
});

Here is the jsFiddle that demonstrates this in action: http://jsfiddle.net/4zwdf/6/

In reality, if we want to be close to what AngularJS is doing the route definition logic should be a bit more complex as AngularJS is also defining a redirect route to correctly handle routes with / at the end (make it effectively optional).

So, while the above technique will work, we need to note the following:

  • This technique depends on the internal implementation and might break if the AngularJS team decides to change the way routes are defined / matched.
  • It is also possible to define the otherwise route using the $route.routes as the default route is stored in the same hash under the null key