33

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).

pkozlowski.opensource
  • 117,202
  • 60
  • 326
  • 286
stej
  • 28,745
  • 11
  • 71
  • 104

2 Answers2

47

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
pkozlowski.opensource
  • 117,202
  • 60
  • 326
  • 286
  • In the 'old' days routes were defined on the fly but the AngularJS team purposefully changed it so that you could only define routes statically up front. I guess the concern is having to trigger a route change if you modify a route that matches the current URL... – Pete BD Nov 01 '12 at 09:49
  • I think the future of routes should be able to cope with this. It may well be waiting for the new module architecture that allows loading modules on the fly? – Pete BD Nov 01 '12 at 09:50
  • 1
    Yes, this is true that once need to be careful with defining a route that matches the current URL (actually one can see this problem in the jsFiddle). The other problem is that we can't define new controllers dynamically (but this is more general issue with the current module system). Anyway, I think that the above is as far as we can get with the current routing system. – pkozlowski.opensource Nov 01 '12 at 09:54
  • Guys, thanks a lot for you response, especially pkozlowski for answer to my question. Can anybody tell me more about 'but this is more general issue with the current module system'? I feel a little bit that the modules are too rigid for me. Maybe I'm wrong, because I'm new to angular, but any clarification that points to weaknesses of angular would be great to (so that people know what to expect). – stej Nov 01 '12 at 11:47
  • stej: the "problem" with the current module system is that you need to download all the AngularJS artifacts (controllers, directives etc.) upfront into a browser. After the initial boostrap of an app it is impossible to add more controllers, directives etc. This question / answer has more info: http://stackoverflow.com/a/12646328/1418796 – pkozlowski.opensource Nov 01 '12 at 11:50
  • Thank you, upvoted. Loading controllers later is possible (just loading a js script with bunch of 'controller functions'). But there is a little limitation that it's not possible to reference them in routes as a function. It must be referenced as a string (`{ templateUrl: .., controller: 'myController'}`, not `{... controller : myController }`), because during route setup, the controller is not loaded yet, so `myController` is undefined. – stej Nov 01 '12 at 14:52
  • I'll wait some time and if nothing changes, I'll accept your answer. Thanks. – stej Nov 01 '12 at 14:52
6

I found that the answer by @pkozlowski.opensource works only in angularjs 1.0.1. However, after angular-route.js becomes an independent file in the later version, directly set the $route doesn't work.

After reviewing the code, I find the key of $route.routes is no longer used to match location but $route.route[key].RegExp is used instead. After I copy the origin when and pathRegExp function, route works. See jsfiddle: http://jsfiddle.net/5FUQa/1/

  function addRoute(path, route) {
     //slightly modified 'when' function in angular-route.js
  }
  addRoute('/dynamic', {
    templateUrl: 'dynamic.tpl.html'
  });
SetupX
  • 413
  • 2
  • 5
  • 13