angularjshottowel

Wiring up a nav menu with Angular routes


I'm working with an asp.net mvc4 and Angular JS (started from John Papa's HotTowel.angular.Breeze SPA). I have added a dropdown menu to the sidebar.html, and have also added the appropriate routes to config.route.js.

I find the tricky part is to properly wire-up the dropdown menus to the Angular routes.

This setup is mostly working, except that when I click on the "MR Reports" dropdown menu below, it's right-away firing a route (i.e. when I hover over MR Reports, it shows the link "localhost:49479/index.html#". In other words, the "MR Reports" should be a dropdown menu and NOT trigger a route change until I choose a dropdown menu item.

I'm not sure if the change should be in config.route.js or in the sidebar.html code.

UPDATE: I'm assuming I can use the Angular ngHref directive, something like ng-href="#{{r.url}}", to dynamically exclude the top-level href tag if there's a sub menu. Perhaps I can use ng-if="r.config.sub" as a guide as well.

Here is the contents of config.route.js (please note the "sub:" section for the sub-menus):

(function () {
  'use strict';

var app = angular.module('app');

// Collect the routes
app.constant('routes', getRoutes());

// Configure the routes and route resolvers
app.config(['$routeProvider', 'routes', routeConfigurator]);
function routeConfigurator($routeProvider, routes) {

    routes.forEach(function (r) {
        $routeProvider.when(r.url, r.config);
    });
    $routeProvider.otherwise({ redirectTo: '/' });
}

// Define the routes 
function getRoutes() {
    return [
        {
            url: '/',
            config: {
                templateUrl: 'app/dashboard/dashboard.html',
                title: 'dashboard',
                settings: {
                    nav: 1,
                    content: '<i class="fa fa-dashboard"></i> Dashboard'
                }
            }
        }, {
            url: '/admin',
            config: {
                title: 'admin',
                templateUrl: 'app/admin/admin.html',
                settings: {
                    nav: 2,
                    content: '<i class="fa fa-lock"></i> Admin'
                }
            }
        },
        {
            url: '/testgrid',
            config: {
                title: 'grids',
                templateUrl: 'app/testgrid/testgrid.html',
                settings: {
                    nav: 3,
                    content: '<i class="fa fa-globe"></i> TestGrid'
                }
            }
        },
        {                
            config: {
                title: 'reports',
                templateUrl: 'app/testgrid/testgrid.html',
                settings: {
                    nav: 4,
                    content: '<i class="fa fa-plus-square "></i> MR Reports <b class="caret"></b>'
                },
                sub: [
                {
                    url: '/testgrid',
                    title: 'reports1',
                    templateUrl: 'app/testgrid/testgrid.html',
                    settings: {
                        content: 'TestGrid'
                    }
                },
                {
                    url: '/admin',
                    title: 'Admin2',
                    templateUrl: 'app/admin/admin.html',
                    settings: {
                        content: 'Admin2'
                    }
                }
                ]
            },
        }           
    ];
 }
})();

and my sidebar.html is defined as :

<div data-cc-sidebar data-ng-controller="sidebar as vm">
<div class="sidebar-filler"></div>
<div class="sidebar-dropdown"><a href="#">Menu</a></div>
<div class="sidebar-inner">
    <div class="sidebar-widget">
    </div>        

    <ul class="navi">                     
        <li class="nlightblue fade-selection-animation"                 
            ng-class="{dropdown: r.config.sub}" 
            data-ng-repeat="r in vm.navRoutes">

            <a href="#{{r.url}}" data-ng-bind-html="r.config.settings.content" ng-class="{'dropdown-toggle': r.config.sub}" data-ng-class="vm.isCurrent(r)" data-toggle="dropdown" ></a>
            <ul ng-if="r.config.sub" class="dropdown-menu">
                <li ng-repeat="submenu in r.config.sub">
                    <a href="#{{submenu.url}}" data-ng-bind-html="submenu.settings.content"></a>
                </li>

            </ul>
        </li>                                                                 
    </ul>         
</div>    


Solution

  • I'm not crazy about my hack, but it ended up working. I used one ng-if to render the href link for non-dropdown menu items, and another ng-if to EXCLUDE the href tag for the dropdowns.

    And in the getRoutes() function, I EXCLUDE the url: property at the top-level of the dropdown.

     <ul class="navi">                   
            <li class="nlightblue fade-selection-animation"                
                ng-class="{dropdown: r.config.sub}"
                data-ng-repeat="r in vm.navRoutes">
    
                <a ng-if="r.url" href="#{{r.url}}" data-ng-bind-html="r.config.settings.content"
                    ng-class="{'dropdown-toggle': r.config.sub}"
                    data-ng-class="vm.isCurrent(r)"
                    data-toggle="dropdown" ></a>
                <a ng-if="r.config.sub" data-ng-bind-html="r.config.settings.content"
                    ng-class="{'dropdown-toggle': r.config.sub}"
                    data-ng-class="vm.isCurrent(r)"
                    data-toggle="dropdown" ></a>
    
                <ul ng-if="r.config.sub" class="dropdown-menu">
                    <li ng-repeat="submenu in r.config.sub">
                        <a href="#{{submenu.url}}" data-ng-bind-html="submenu.settings.content"></a>
                    </li>
    
                </ul>
            </li>   
    

    config.route.js function:

     function getRoutes() {
        return [
            {
                url: '/',
                config: {
                    templateUrl: 'app/dashboard/dashboard.html',
                    title: 'dashboard',
                    settings: {
                        nav: 1,
                        content: '<i class="fa fa-dashboard"></i> Dashboard'
                    }
                }
            }, {
                url: '/admin',
                config: {
                    title: 'admin',
                    templateUrl: 'app/admin/admin.html',
                    settings: {
                        nav: 2,
                        content: '<i class="fa fa-lock"></i> Admin'
                    }
                }
            },
            {
                url: '/testgrid',
                config: {
                    title: 'grids',
                    templateUrl: 'app/testgrid/testgrid.html',
                    settings: {
                        nav: 3,
                        content: '<i class="fa fa-globe"></i> TestGrid'
                    }
                }
            },
            {   // Exclude url: at this top level
                config: {
                    title: 'reports',                    
                    settings: {
                        nav: 4,
                        content: '<i class="fa fa-plus-square "></i> MR Reports <b class="caret"></b>'
                    },
                    sub: [
                    {
                        url: '/testgrid',
                        title: 'reports1',
                        templateUrl: 'app/testgrid/testgrid.html',
                        settings: {
                            content: 'TestGrid'
                        }
                    },
                    {
                        url: '/admin',
                        title: 'Admin2',
                        templateUrl: 'app/admin/admin.html',
                        settings: {
                            content: 'Admin2'
                        }
                    }
                    ]
                },
            }           
        ];
    }