Angular JS and Dynamic Menu – Part 2

I have blogged previously on how to build a menu with Angular where menu data comes from the database.  In this post I will elaborate a bit on my technique, revise it some and make adjustments for log in process.  The basic premise is sill the same – I need to get my data from the database, but this time I want to postpone the process until after the login.

So, in my main module I only configure a single route – one to my login screen.

        .config(['$routeProvider', 'globalsServiceProvider',
            function ($routeProvider: ng.route.IRouteProvider, globalsServiceProvider: interfaces.IGlobalsProvider) {
                var globals: interfaces.IGLobals = globalsServiceProvider.$get();
                $routeProvider.when('/login', {
                    controller: 'loginController',
                    templateUrl: globals.baseUrl + 'login/logon'
                    redirectTo: '/login'

This code is similar to the one I posted prior.  The only thing worth noting is that I am using absolute URL for my menus by attaching site root URL to relative view’s path from my database.  Next step is to create home controller with will do a few things:

  • Get menu from a service that talks to Web Api controller
  • Raise an event on the root scope that menu has been retrieved, passing the menu itself as an event argument.
  • Listen to an event that routes have been built and issue initial navigation to home page.

The reason I am using root scope is because it is visible everywhere, and I need a scope to exchange my events.  Seconds step is to use config() function on the same home module to build routes based on my menu from my database.  Of course, after I create route structure I want to navigate somewhere.  Hence, my config block does the following:

  • Listen to the event that menu has been retrieved
  • Populate routes
  • Raise an event that routes have been populated. 

Again, I am using root scope for all the events.  There is one interesting thing about config() function in the module.  I cannot inject instances into it, just providers.  Hence, I cannot even get the root scope.  To get around this issue I want to use angular.injector().  This presents a different problem.  With every call I get a new instance of the injector, hence my root scope from one call is not the same as the one that is injected into the controller by different injector intance.  So, I need to use another workaround.  To guarantee that I get the same injector instance, I need to scrub it off my root HTML element that is decorated with ng-app attribute.  This way I will get instance of exact same objects as the rest of my application.  So, let’s take a look at the controller:

    class homeController extends app.core.controllers.CoreController {
            $location: ng.ILocationService,
            $scope: IHomeScope,
            globalsService: interfaces.IGLobals,
            $rootScope: ng.IRootScopeService) {
            $scope.navigate = function (menu: app.home.models.IMenuItem) {
            $scope.searchText = '';
            $ = () => {
                console.log('looking for ' + $scope.searchText);
            $scope.applicationName = globalsService.applicationName.toLocaleLowerCase();
            $scope.$on('loggedIn', () => {
                menuService.getMenu((menu: IMenuItem[]) => {
                    $scope.menuItems = menu;
                    $rootScope.$on('routesLoaded', () => {
                    $rootScope.$broadcast('menuLoaded', menu);

The key thing to notice is that I get an instance of the root scope and subscribe to login event to kick off menu building. In the logged in event handler I get the menu via menu service.  In the handler for that (function that is passed into to getMenu call) I am saving my menu items, then subscribe to ‘routes loaded’ event, and finally broadcast (raise) an event that the menu has been retrieved.  In my ‘routes loaded’ hander I simply navigate to home page by providing an invalid route, thus going to home page specified in otherwise() call to route provider. 

Now, in config block I do the opposite subscriptions:

.config(['$routeProvider', function ($routeProvider: ng.route.IRouteProvider) {
            var injector: = angular.injector(['ng']);
            var timeout: ng.ITimeoutService = injector.get('$timeout');
            var stop;
            var wait = () => {
                stop = timeout(() => {
                    var finalInjector: = angular.element('[data-ng-app="app"]').injector();
                    if (finalInjector) {
                        var scope: ng.IScope = finalInjector.get('$rootScope');
                        scope.$on('menuLoaded', (event: ng.IAngularEvent, ...args: any[]) => {
                            var menu = <IMenuItem[]>args[0];
                            var globals: interfaces.IGLobals = finalInjector.get('globalsService');
                            angular.forEach(menu, (item: IMenuItem) => {
                                if (item.Route) {
                                    $routeProvider.when(item.Route, {
                                        controller: item.Controller,
                                        templateUrl: globals.baseUrl + item.View
                                redirectTo: '/home'

                    } else {
                }, 10);

This code is a bit more convoluted, and it has everything to do with the fact that I cannot inject instances, just providers, into config block.  First, I get an injector service by calling angular.injector().  The only thing I need is timeout service.  I kick off a function every 10 ms that does just one thing – monitors my root HTML element until it has injector populated, which will happen after Angular application is bootstrapped.  This injector is shared for entire application, hence I use it to get an instance or the root scope.  I use the root scope to subscribe and raise events to communicate with my controller.

To summarize, I do not want to have routes injected until after the login process because menu is dynamic for each user.  Because I postpone the routes creation until after the login, I can make sure that only the routes the user has rights to are created.  I also use events to communicate the state of the application between different objects – controller and configuration block.



  1. Pingback: Angular Dynamic Menu and Initial URL | Sergey Barskiy's Blog

  2. Pingback: Angular JS and Dynamic Menu – Part 2 | Se...

Leave a Reply

Your email address will not be published. Required fields are marked *