OdeToCode IC Logo

Highlighting the Active Menu Item In AngularJS

Friday, June 7, 2013

Most applications feature menu items that will need to appear selected at the appropriate time.

How to do this with Angular?

There are many different approaches, but ideally we’ll do this with a directive, since directives are responsible for DOM manipulation, and “selecting” a menu item will require some manipulation of the classes on an element. The goal would be to make it as easy as possible from a view:

<nav>
    <ul data-active-menu="selected">
        <li><a href="#/details?q=foo">Details</a></li>
        <li><a href="#/test">Summary</a></li>
        ...
    </ul>
</nav>

In the above code, the data-active-menu attribute should allow a custom directive to jump in and manipulate the styles of the elements inside by adding “selected” as a class to the active link, and removing the class from all other links.

The directive code itself would look like this:

(function () {
    
    var makeWatcher = function(location) {
        return function() {
            return location.url();
        };
    };

    var makeLinkUpdater = function(links, className) {
        return function (value) {
            angular.forEach(links, function(link) {
                link = angular.element(link);
                if (/\#(\/[^\/]+)/.exec(link.attr("href"))[1] == value) {
                    link.addClass(className);
                } else {
                    link.removeClass(className);
                }
            });
        };
    };

    var activeMenu = function($location) {

        var link = function(scope, element, attrs) {
            var links = element.find("a");
            var className = attrs.activeMenu;
            scope.$watch(makeWatcher($location),
                         makeLinkUpdater(links, className));
        };

        return {
            link:link
        };
    };
    activeMenu.$injector = ["$location"];
    
    angular.module("testApp")
           .directive("activeMenu", activeMenu);
}());

By taking a dependency on the $location service in AngularJS, the directive can both watch for when the location changes, and compare the current location to the href in the menu items. Matching hrefs get the class specified in the data-menu-active attribute. Easy, reusable, and keeps more boilerplate code out of the controllers.