Как выделить текущий пункт меню?

помогает ли AngularJS каким-либо образом установить active класс по ссылке для текущей страницы?

Я думаю, что есть какой-то волшебный способ сделать это, но я не могу найти.

мое меню выглядит так:

 <ul>
   <li><a class="active" href="/tasks">Tasks</a>
   <li><a href="/actions">Tasks</a>
 </ul>

и у меня есть контроллеры для каждого из них в мои маршруты: TasksController и ActionsController.

но я не могу найти способ привязать "активный" класс к a ссылки на контроллеры.

какие-то намеки?

30 ответов


on view

<a ng-class="getClass('/tasks')" href="/tasks">Tasks</a>

контроллер

$scope.getClass = function (path) {
  return ($location.path().substr(0, path.length) === path) ? 'active' : '';
}

при этом ссылка "Задачи" будет иметь активный класс в любом url-адресе, который начинается с "/ tasks"(например, "/tasks/1/reports")


Я предлагаю использовать директиву по ссылке.

но это еще не идеально. Следите за hashbangs ;)

вот javascript для директивы:

angular.module('link', []).
  directive('activeLink', ['$location', function (location) {
    return {
      restrict: 'A',
      link: function(scope, element, attrs, controller) {
        var clazz = attrs.activeLink;
        var path = attrs.href;
        path = path.substring(1); //hack because path does not return including hashbang
        scope.location = location;
        scope.$watch('location.path()', function (newPath) {
          if (path === newPath) {
            element.addClass(clazz);
          } else {
            element.removeClass(clazz);
          }
        });
      }
    };
  }]);

а вот как он будет использоваться в HTML:

<div ng-app="link">
  <a href="#/one" active-link="active">One</a>
  <a href="#/two" active-link="active">One</a>
  <a href="#" active-link="active">home</a>
</div>

после укладки с помощью css:

.active { color: red; }

вот простой подход, который хорошо работает с углового.

<ul>
    <li ng-class="{ active: isActive('/View1') }"><a href="#/View1">View 1</a></li>
    <li ng-class="{ active: isActive('/View2') }"><a href="#/View2">View 2</a></li>
    <li ng-class="{ active: isActive('/View3') }"><a href="#/View3">View 3</a></li>
</ul>

в вашем контроллере AngularJS:

$scope.isActive = function (viewLocation) {
     var active = (viewLocation === $location.path());
     return active;
};

этот поток имеет ряд других подобных ответов.

Как установить активный класс начальной загрузки navbar с Angular JS?


просто чтобы добавить мои два цента в дебатах, я сделал чистый угловой модуль (без jQuery), и он также будет работать с хэш-URL-адресами, содержащими данные. (например,#/this/is/path?this=is&some=data)

вы просто добавляете модуль как зависимость и auto-active одному из предков меню. Вот так:

<ul auto-active>
    <li><a href="#/">main</a></li>
    <li><a href="#/first">first</a></li>
    <li><a href="#/second">second</a></li>
    <li><a href="#/third">third</a></li>
</ul>

и модуль выглядит так:

(function () {
    angular.module('autoActive', [])
        .directive('autoActive', ['$location', function ($location) {
        return {
            restrict: 'A',
            scope: false,
            link: function (scope, element) {
                function setActive() {
                    var path = $location.path();
                    if (path) {
                        angular.forEach(element.find('li'), function (li) {
                            var anchor = li.querySelector('a');
                            if (anchor.href.match('#' + path + '(?=\?|$)')) {
                                angular.element(li).addClass('active');
                            } else {
                                angular.element(li).removeClass('active');
                            }
                        });
                    }
                }

                setActive();

                scope.$on('$locationChangeSuccess', setActive);
            }
        }
    }]);
}());

(вы можете, конечно, просто использовать директивную часть)

стоит также отметить, что это не работает для пустых хэшей (например,example.com/# или просто example.com) он должен иметь по крайней мере example.com/#/ или просто example.com#/. Но это происходит автоматически с ngResource и тому подобное.

и вот скрипка:http://jsfiddle.net/gy2an/8/


в моем случае я решил эту проблему, создав простой контроллер, ответственный за навигацию

angular.module('DemoApp')
  .controller('NavigationCtrl', ['$scope', '$location', function ($scope, $location) {
    $scope.isCurrentPath = function (path) {
      return $location.path() == path;
    };
  }]);

и просто добавив ng-класс к элементу так:

<ul class="nav" ng-controller="NavigationCtrl">
  <li ng-class="{ active: isCurrentPath('/') }"><a href="#/">Home</a></li>
  <li ng-class="{ active: isCurrentPath('/about') }"><a href="#/about">About</a></li>
  <li ng-class="{ active: isCurrentPath('/contact') }"><a href="#/contact">Contact</a></li>
</ul>

есть ng-class директива, которая связывает переменную и класс css. Он также принимает объект (пары className vs bool value).

вот пример,http://plnkr.co/edit/SWZAqj


на ответ от @Renan-tomal-fernandes хорошо, но необходимо несколько улучшений для правильной работы. Как бы то ни было, он всегда обнаружит ссылку на домашнюю страницу ( / ), даже если вы находитесь в другом разделе.

поэтому я немного улучшил его, вот код. Я работаю с загрузки таким образом, активная часть находится в <li> элемент вместо <a>.

контроллер

$scope.getClass = function(path) {
    var cur_path = $location.path().substr(0, path.length);
    if (cur_path == path) {
        if($location.path().substr(0).length > 1 && path.length == 1 )
            return "";
        else
            return "active";
    } else {
        return "";
    }
}

шаблон

<div class="nav-collapse collapse">
  <ul class="nav">
    <li ng-class="getClass('/')"><a href="#/">Home</a></li>
    <li ng-class="getClass('/contents/')"><a href="#/contests/">Contents</a></li>
    <li ng-class="getClass('/data/')"><a href="#/data/">Your data</a></li>
  </ul>
</div>

на AngularUI маршрутизатор пользователей:

<a ui-sref-active="active" ui-sref="app">

и что будет active класс выбранного объекта.


вот решение, которое я придумал после прочтения некоторых отличные предложения выше. В моей конкретной ситуации я пытался использовать компонент загрузочных вкладок как мое меню, но не хотел использовать версию Angular-UI этого, потому что я хочу, чтобы вкладки действовали как меню, где каждая вкладка имеет возможность закладки, а не вкладки, действующие как навигация для одной страницы. (См. http://angular-ui.github.io/bootstrap/#/tabs Если вы заинтересованы в том, что Angular-UI версия загрузочных вкладок выглядит так).

мне очень понравился ответ kfis о создании собственной директивы для обработки этого, однако казалось громоздким иметь директиву, которая должна быть размещена на каждой отдельной ссылке. Поэтому я создал свою собственную угловую директиву, которая помещается вместо этого один раз на ul. Но если кто-то еще пытается сделать то же самое, я думал, что я отправлю его здесь, хотя, как я уже сказал, Многие из вышеперечисленных решений, а также. Это немного больше сложное решение, насколько javascript идет, но он создает многоразовый компонент с минимальной разметкой.

вот javascript для директивы и поставщика маршрута для ng:view:

var app = angular.module('plunker', ['ui.bootstrap']).
  config(['$routeProvider', function($routeProvider) {
    $routeProvider.
        when('/One', {templateUrl: 'one.html'}).
        when('/Two', {templateUrl: 'two.html'}).
        when('/Three', {templateUrl: 'three.html'}).
        otherwise({redirectTo: '/One'});
  }]).
  directive('navTabs', ['$location', function(location) {
    return {
        restrict: 'A',
        link: function(scope, element) {
            var $ul = $(element);
            $ul.addClass("nav nav-tabs");

            var $tabs = $ul.children();
            var tabMap = {};
            $tabs.each(function() {
              var $li = $(this);
              //Substring 1 to remove the # at the beginning (because location.path() below does not return the #)
              tabMap[$li.find('a').attr('href').substring(1)] = $li;
            });

            scope.location = location;
            scope.$watch('location.path()', function(newPath) {
                $tabs.removeClass("active");
                tabMap[newPath].addClass("active");
            });
        }

    };

 }]);

тогда в вашем html вы просто:

<ul nav-tabs>
  <li><a href="#/One">One</a></li>
  <li><a href="#/Two">Two</a></li>
  <li><a href="#/Three">Three</a></li>
</ul>
<ng:view><!-- Content will appear here --></ng:view>

вот планкер для него:http://plnkr.co/edit/xwGtGqrT7kWoCKnGDHYN?p=preview.


вы можете реализовать это очень просто, вот пример:

<div ng-controller="MenuCtrl">
  <ul class="menu">
    <li ng-class="menuClass('home')"><a href="#home">Page1</a></li>
    <li ng-class="menuClass('about')"><a href="#about">Page2</a></li>
  </ul>

</div>

и ваш контроллер должен быть такой:

app.controller("MenuCtrl", function($scope, $location) {
  $scope.menuClass = function(page) {
    var current = $location.path().substring(1);
    return page === current ? "active" : "";
  };
});

использовать угловые-интерфейс-интерфейс маршрутизатора-сref-активный директива https://github.com/angular-ui/ui-router/wiki/Quick-Reference#statename

<ul>
  <li ui-sref-active="active" class="item">
    <a href ui-sref="app.user({user: 'bilbobaggins'})">@bilbobaggins</a>
  </li>
  <!-- ... -->
</ul>

у меня была аналогичная проблема с находится за пределами контроллер рамки. Не уверен, что это лучшее решение или рекомендуемое, но это то, что сработало для меня. Я добавил следующее в свою конфигурацию приложения:

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

app.run(function($rootScope, $location){
  $rootScope.menuActive = function(url, exactMatch){
    if (exactMatch){
      return $location.path() == url;
    }
    else {
      return $location.path().indexOf(url) == 0;
    }
  }
});

затем в представлении у меня есть:

<li><a href="/" ng-class="{true: 'active'}[menuActive('/', true)]">Home</a></li>
<li><a href="/register" ng-class="{true: 'active'}[menuActive('/register')]">
<li>...</li>

используя директиву (так как мы делаем манипуляции DOM здесь), следующее, вероятно, ближе всего к тому, чтобы делать вещи "угловым способом":

$scope.timeFilters = [
  {'value':3600,'label':'1 hour'},
  {'value':10800,'label':'3 hours'},
  {'value':21600,'label':'6 hours'},
  {'value':43200,'label':'12 hours'},
  {'value':86400,'label':'24 hours'},
  {'value':604800,'label':'1 week'}
]

angular.module('whatever', []).directive('filter',function(){
return{
    restrict: 'A',
    template: '<li ng-repeat="time in timeFilters" class="filterItem"><a ng-click="changeTimeFilter(time)">{{time.label}}</a></li>',
    link: function linkFn(scope, lElement, attrs){

        var menuContext = attrs.filter;

        scope.changeTimeFilter = function(newTime){
          scope.selectedtimefilter = newTime;

        }

        lElement.bind('click', function(cevent){
            var currentSelection = angular.element(cevent.srcElement).parent();
            var previousSelection = scope[menuContext];

            if(previousSelection !== currentSelection){
                if(previousSelection){
                    angular.element(previousSelection).removeClass('active')
                }
                scope[menuContext] = currentSelection;

                scope.$apply(function(){
                    currentSelection.addClass('active');
                })
            }
        })
    }
}
})

тогда ваш HTML будет выглядеть так:

<ul class="dropdown-menu" filter="times"></ul>

Я сделал это так:

var myApp = angular.module('myApp', ['ngRoute']);

myApp.directive('trackActive', function($location) {
    function link(scope, element, attrs){
        scope.$watch(function() {
            return $location.path();
        }, function(){
            var links = element.find('a');
            links.removeClass('active');
            angular.forEach(links, function(value){
                var a = angular.element(value);
                if (a.attr('href') == '#' + $location.path() ){
                    a.addClass('active');
                }
            });
        });
    }
    return {link: link};
});

Это позволяет вам иметь ссылки в разделе, который имеет директиву track-active:

<nav track-active>
     <a href="#/">Page 1</a>
     <a href="#/page2">Page 2</a>
     <a href="#/page3">Page 3</a>
</nav>

этот подход кажется мне намного чище, чем другие.

кроме того, если вы используете jQuery, вы можете сделать его намного аккуратнее, потому что jQlite имеет только базовую поддержку селектора. Гораздо более чистая версия с jQuery, включенная до angular include, будет выглядеть так:

myApp.directive('trackActive', function($location) {
    function link(scope, element, attrs){
        scope.$watch(function() {
            return $location.path();
        }, function(){
            element.find('a').removeClass('active').find('[href="#'+$location.path()+'"]').addClass('active');
        });
    }
    return {link: link};
});

здесь jsFiddle


мое решение этой проблемы, используйте route.current в угловом шаблоне.

как /tasks маршрут, чтобы выделить в меню, вы можете добавить свою собственную собственность menuItem к маршрутам, объявленным вашим модулем:

$routeProvider.
  when('/tasks', {
    menuItem: 'TASKS',
    templateUrl: 'my-templates/tasks.html',
    controller: 'TasksController'
  );

тогда в вашем шаблоне tasks.htmlвы можете использовать следующие


вот расширение директивы kfis, которое я сделал, чтобы разрешить различные уровни соответствия пути. По сути, я обнаружил необходимость сопоставления путей URL до определенной глубины, поскольку точное сопоставление не позволяет перенаправлять вложенность и состояние по умолчанию. Надеюсь, это поможет.

    .directive('selectedLink', ['$location', function(location) {
    return {
        restrict: 'A',
        scope:{
            selectedLink : '='
            },
        link: function(scope, element, attrs, controller) {
            var level = scope.selectedLink;
            var path = attrs.href;
            path = path.substring(1); //hack because path does not return including hashbang
            scope.location = location;
            scope.$watch('location.path()', function(newPath) {
                var i=0;
                p = path.split('/');
                n = newPath.split('/');
                for( i ; i < p.length; i++) { 
                    if( p[i] == 'undefined' || n[i] == 'undefined' || (p[i] != n[i]) ) break;
                    }

                if ( (i-1) >= level) {
                    element.addClass("selected");
                    } 
                else {
                    element.removeClass("selected");
                    }
                });
            }

        };
    }]);

и вот как я использую ссылку

<nav>
    <a href="#/info/project/list"  selected-link="2">Project</a>
    <a href="#/info/company/list" selected-link="2">Company</a>
    <a href="#/info/person/list"  selected-link="2">Person</a>
</nav>

эта директива будет соответствовать уровню глубины, указанному в значении атрибута для директивы. Просто означает, что он может быть использован в другом месте многие раз.


вот еще одна директива для выделения активной ссылки.

основные характеристики:

  • отлично работает с href, который содержит динамические угловые выражения
  • совместимость с навигацией hash-bang
  • совместим с Bootstrap, где активный класс должен применяться к родительскому li не сама ссылка
  • позволяет сделать ссылку активной, если какой-либо вложенный путь активен
  • позволяет сделать ссылку отключенной, если это не так активный

код:

.directive('activeLink', ['$location', 
function($location) {
    return {
        restrict: 'A',
        link: function(scope, elem, attrs) {
            var path = attrs.activeLink ? 'activeLink' : 'href';
            var target = angular.isDefined(attrs.activeLinkParent) ? elem.parent() : elem;
            var disabled = angular.isDefined(attrs.activeLinkDisabled) ? true : false;
            var nested = angular.isDefined(attrs.activeLinkNested) ? true : false;

            function inPath(needle, haystack) {
                var current = (haystack == needle);
                if (nested) {
                    current |= (haystack.indexOf(needle + '/') == 0);
                }

                return current;
            }

            function toggleClass(linkPath, locationPath) {
                // remove hash prefix and trailing slashes
                linkPath = linkPath ? linkPath.replace(/^#!/, '').replace(/\/+$/, '') : '';
                locationPath = locationPath.replace(/\/+$/, '');

                if (linkPath && inPath(linkPath, locationPath)) {
                    target.addClass('active');
                    if (disabled) {
                        target.removeClass('disabled');
                    }
                } else {
                    target.removeClass('active');
                    if (disabled) {
                        target.addClass('disabled');
                    }
                }
            }

            // watch if attribute value changes / evaluated
            attrs.$observe(path, function(linkPath) {
                toggleClass(linkPath, $location.path());
            });

            // watch if location changes
            scope.$watch(
                function() {
                    return $location.path(); 
                }, 
                function(newPath) {
                    toggleClass(attrs[path], newPath);
                }
            );
        }
    };
}
]);

использование:

простой пример с угловым выражением, скажем $scope.var = 2, то ссылка будет активна, если местоположение / url / 2 :

<a href="#!/url/{{var}}" active-link>

пример Bootstrap, Родительский li получит активный класс:

<li>
    <a href="#!/url" active-link active-link-parent>
</li>

пример с вложенными URL-адресами ссылка будет активна, если какой-либо вложенный url-адрес активен (т. е. / url / 1, / url / 2, url/1/2/ - ... ..)

<a href="#!/url" active-link active-link-nested>

сложный пример, ссылка указывает на один URL (/и url1), но будет активен, если выбран другой (/url2):

<a href="#!/url1" active-link="#!/url2" active-link-nested>

пример с отключенной ссылкой, если он не активен, у него будет 'disabled' класс:

<a href="#!/url" active-link active-link-disabled>

все активный-ссылка-* атрибуты могут быть использованы в любой комбинации, поэтому могут быть реализованы очень сложные условия.


Если вам нужны ссылки для директивы в оболочке, а не выбор каждой отдельной ссылки (облегчает просмотр области в Batarang), это тоже работает довольно хорошо:

  angular.module("app").directive("navigation", [
    "$location", function($location) {
      return {
        restrict: 'A',
        scope: {},
        link: function(scope, element) {
          var classSelected, navLinks;

          scope.location = $location;

          classSelected = 'selected';

          navLinks = element.find('a');

          scope.$watch('location.path()', function(newPath) {
            var el;
            el = navLinks.filter('[href="' + newPath + '"]');

            navLinks.not(el).closest('li').removeClass(classSelected);
            return el.closest('li').addClass(classSelected);
          });
        }
      };
    }
  ]);

разметка будет просто:

    <nav role="navigation" data-navigation>
        <ul>
            <li><a href="/messages">Messages</a></li>
            <li><a href="/help">Help</a></li>
            <li><a href="/details">Details</a></li>
        </ul>
    </nav>

Я также должен упомянуть, что я использую "полный жир" jQuery в этом примере, но вы можете легко изменить то, что я сделал с фильтрацией и так далее.


согласно ответу @kfis, это комментарии и моя рекомендация, заключительная директива, как показано ниже:

.directive('activeLink', ['$location', function (location) {
    return {
      restrict: 'A',
      link: function(scope, element, attrs, controller) {
        var clazz = attrs.activeLink;        
        var path = attrs.href||attrs.ngHref;
        path = path.substring(1); //hack because path does not return including hashbang
        scope.location = location;
        scope.$watch('window.location.href', function () {
          var newPath = (window.location.pathname + window.location.search).substr(1);
          if (path === newPath) {
            element.addClass(clazz);
          } else {
            element.removeClass(clazz);
          }
        });
      }
    };
  }]);

а вот как он будет использоваться в HTML:

<div ng-app="link">
  <a href="#/one" active-link="active">One</a>
  <a href="#/two" active-link="active">One</a>
  <a href="#" active-link="active">home</a>
</div>

после укладки с помощью css:

.active { color: red; }

для тех, кто использует ui-router, мой ответ несколько похож на Ender2050, но я предпочитаю делать это с помощью тестирования имени состояния:

$scope.isActive = function (stateName) {
  var active = (stateName === $state.current.name);
  return active;
};

соответствующий HTML:

<ul class="nav nav-sidebar">
    <li ng-class="{ active: isActive('app.home') }"><a ui-sref="app.home">Dashboard</a></li>
    <li ng-class="{ active: isActive('app.tiles') }"><a ui-sref="app.tiles">Tiles</a></li>
</ul>

ничего из вышеперечисленного директива предложения были полезны для меня. Если у вас есть bootstrap navbar, как это

<ul class="nav navbar-nav">
    <li><a ng-href="#/">Home</a></li>
    <li><a ng-href="#/about">About</a></li>
  ...
</ul>

(это может быть $ yo angular startup), то вы хотите добавить .active до родитель <li> список классов элементов, а не сам элемент; i.e <li class="active">..</li>. Вот что я написал:--17-->

.directive('setParentActive', ['$location', function($location) {
  return {
    restrict: 'A',
    link: function(scope, element, attrs, controller) {
      var classActive = attrs.setParentActive || 'active',
          path = attrs.ngHref.replace('#', '');
      scope.location = $location;
      scope.$watch('location.path()', function(newPath) {
        if (path == newPath) {
          element.parent().addClass(classActive);
        } else {
          element.parent().removeClass(classActive);
        }
      })
    }
  }
}])

использование set-parent-active; .active по умолчанию, поэтому не нужно устанавливать

<li><a ng-href="#/about" set-parent-active>About</a></li>

и родителем <li> элемент будь .active когда ссылка активна. Использовать альтернативу .active класс .highlight, просто

<li><a ng-href="#/about" set-parent-active="highlight">About</a></li>

использование угловой версии 6 с Bootstrap 4.1

я смог сделать это, как показано ниже.

в приведенном ниже примере, когда URL-адрес видит "/ contact", Bootstrap active затем добавляется в HTML-тег. Когда URL-адрес изменяется, он удаляется.

<ul>
<li class="nav-item" routerLink="/contact" routerLinkActive="active">
    <a class="nav-link" href="/contact">Contact</a>
</li>
</ul>

эта директива позволяет добавить класс CSS к элементу, когда ссылка маршрут становится активным.

подробнее о Угловое сайт


самое главное для меня было не менять вообще код начальной загрузки по умолчанию. Вот мой контроллер меню, который ищет параметры меню, а затем добавляет поведение, которое мы хотим.

file: header.js
function HeaderCtrl ($scope, $http, $location) {
  $scope.menuLinkList = [];
  defineFunctions($scope);
  addOnClickEventsToMenuOptions($scope, $location);
}

function defineFunctions ($scope) {
  $scope.menuOptionOnClickFunction = function () {
    for ( var index in $scope.menuLinkList) {
      var link = $scope.menuLinkList[index];
      if (this.hash === link.hash) {
        link.parentElement.className = 'active';
      } else {
        link.parentElement.className = '';
      }
    }
  };
}

function addOnClickEventsToMenuOptions ($scope, $location) {
  var liList = angular.element.find('li');
  for ( var index in liList) {
    var liElement = liList[index];
    var link = liElement.firstChild;
    link.onclick = $scope.menuOptionOnClickFunction;
    $scope.menuLinkList.push(link);
    var path = link.hash.replace("#", "");
    if ($location.path() === path) {
      link.parentElement.className = 'active';
    }
  }
}

     <script src="resources/js/app/header.js"></script>
 <div class="navbar navbar-fixed-top" ng:controller="HeaderCtrl">
    <div class="navbar-inner">
      <div class="container-fluid">
        <button type="button" class="btn btn-navbar" data-toggle="collapse" data-target=".nav-collapse">
          <span class="icon-bar"></span> <span class="icon-bar"></span> 
<span     class="icon-bar"></span>
        </button>
        <a class="brand" href="#"> <img src="resources/img/fom-logo.png"
          style="width: 80px; height: auto;">
        </a>
        <div class="nav-collapse collapse">
          <ul class="nav">
            <li><a href="#/platforms">PLATFORMS</a></li>
            <li><a href="#/functionaltests">FUNCTIONAL TESTS</a></li>
          </ul> 
        </div>
      </div>
    </div>
  </div>

была такая же проблема. Вот мой решение:

.directive('whenActive',
  [
    '$location',
    ($location)->
      scope: true,
      link: (scope, element, attr)->
        scope.$on '$routeChangeSuccess', 
          () ->
            loc = "#"+$location.path()
            href = element.attr('href')
            state = href.indexOf(loc)
            substate = -1

            if href.length > 3
              substate = loc.indexOf(href)
            if loc.length is 2
              state = -1

            #console.log "Is Loc: "+loc+" in Href: "+href+" = "+state+" and Substate = "+substate

            if state isnt -1 or substate isnt -1
              element.addClass 'selected'
              element.parent().addClass 'current-menu-item'
            else if href is '#' and loc is '#/'
              element.addClass 'selected'
              element.parent().addClass 'current-menu-item'
            else
              element.removeClass 'selected'
              element.parent().removeClass 'current-menu-item'
  ])

вот мои два цента, это работает просто отлично.

Примечание: это не соответствует дочерним страницам (что мне нужно).

вид:

<a ng-class="{active: isCurrentLocation('/my-path')}"  href="/my-path" >
  Some link
</a>


Я только что написал директиву для этого.

использование:

<ul class="nav navbar-nav">
  <li active><a href="#/link1">Link 1</a></li>
  <li active><a href="#/link2">Link 2</a></li>
</ul>

реализация:

angular.module('appName')
  .directive('active', function ($location, $timeout) {
    return {
      restrict: 'A',
      link: function (scope, element, attrs) {
        // Whenever the user navigates to a different page...
        scope.$on('$routeChangeSuccess', function () {
          // Defer for other directives to load first; this is important
          // so that in case other directives are used that this directive
          // depends on, such as ng-href, the href is evaluated before
          // it's checked here.
          $timeout(function () {
            // Find link inside li element
            var $link = element.children('a').first();

            // Get current location
            var currentPath = $location.path();

            // Get location the link is pointing to
            var linkPath = $link.attr('href').split('#').pop();

            // If they are the same, it means the user is currently
            // on the same page the link would point to, so it should
            // be marked as such
            if (currentPath === linkPath) {
              $(element).addClass('active');
            } else {
              // If they're not the same, a li element that is currently
              // marked as active needs to be "un-marked"
              element.removeClass('active');
            }
          });
        });
      }
    };
  });

тесты:

'use strict';

describe('Directive: active', function () {

  // load the directive's module
  beforeEach(module('appName'));

  var element,
      scope,
      location,
      compile,
      rootScope,
      timeout;

  beforeEach(inject(function ($rootScope, $location, $compile, $timeout) {
    scope = $rootScope.$new();
    location = $location;
    compile = $compile;
    rootScope = $rootScope;
    timeout = $timeout;
  }));

  describe('with an active link', function () {
    beforeEach(function () {
      // Trigger location change
      location.path('/foo');
    });

    describe('href', function () {
      beforeEach(function () {
        // Create and compile element with directive; note that the link
        // is the same as the current location after the location change.
        element = angular.element('<li active><a href="#/foo">Foo</a></li>');
        element = compile(element)(scope);

        // Broadcast location change; the directive waits for this signal
        rootScope.$broadcast('$routeChangeSuccess');

        // Flush timeout so we don't have to write asynchronous tests.
        // The directive defers any action using a timeout so that other
        // directives it might depend on, such as ng-href, are evaluated
        // beforehand.
        timeout.flush();
      });

      it('adds the class "active" to the li', function () {
        expect(element.hasClass('active')).toBeTruthy();
      });
    });

    describe('ng-href', function () {
      beforeEach(function () {
        // Create and compile element with directive; note that the link
        // is the same as the current location after the location change;
        // however this time with an ng-href instead of an href.
        element = angular.element('<li active><a ng-href="#/foo">Foo</a></li>');
        element = compile(element)(scope);

        // Broadcast location change; the directive waits for this signal
        rootScope.$broadcast('$routeChangeSuccess');

        // Flush timeout so we don't have to write asynchronous tests.
        // The directive defers any action using a timeout so that other
        // directives it might depend on, such as ng-href, are evaluated
        // beforehand.
        timeout.flush();
      });

      it('also works with ng-href', function () {
        expect(element.hasClass('active')).toBeTruthy();
      });
    });
  });

  describe('with an inactive link', function () {
    beforeEach(function () {
      // Trigger location change
      location.path('/bar');

      // Create and compile element with directive; note that the link
      // is the NOT same as the current location after the location change.
      element = angular.element('<li active><a href="#/foo">Foo</a></li>');
      element = compile(element)(scope);

      // Broadcast location change; the directive waits for this signal
      rootScope.$broadcast('$routeChangeSuccess');

      // Flush timeout so we don't have to write asynchronous tests.
      // The directive defers any action using a timeout so that other
      // directives it might depend on, such as ng-href, are evaluated
      // beforehand.
      timeout.flush();
    });

    it('does not add the class "active" to the li', function () {
      expect(element.hasClass('active')).not.toBeTruthy();
    });
  });

  describe('with a formerly active link', function () {
    beforeEach(function () {
      // Trigger location change
      location.path('/bar');

      // Create and compile element with directive; note that the link
      // is the same as the current location after the location change.
      // Also not that the li element already has the class "active".
      // This is to make sure that a link that is active right now will
      // not be active anymore when the user navigates somewhere else.
      element = angular.element('<li class="active" active><a href="#/foo">Foo</a></li>');
      element = compile(element)(scope);

      // Broadcast location change; the directive waits for this signal
      rootScope.$broadcast('$routeChangeSuccess');

      // Flush timeout so we don't have to write asynchronous tests.
      // The directive defers any action using a timeout so that other
      // directives it might depend on, such as ng-href, are evaluated
      // beforehand.
      timeout.flush();
    });

    it('removes the "active" class from the li', function () {
      expect(element.hasClass('active')).not.toBeTruthy();
    });
  });
});

маршрут:

$routeProvider.when('/Account/', { templateUrl: '/Home/Account', controller: 'HomeController' });

меню html:

<li id="liInicio" ng-class="{'active':url=='account'}">

контроллер:

angular.module('Home').controller('HomeController', function ($scope, $http, $location) {
    $scope.url = $location.url().replace(/\//g, "").toLowerCase();
...

проблемы я нашел здесь то, что пункт меню активен только при полной загрузке страницы. При загрузке частичного представления меню не изменяется. Кто-нибудь знает, почему это происходит?


вот гораздо лучший способ сделать это

<ul>
    <li ng-class="{ active: isActive('/tasks')}"><a href="/">Tasks</a></li>
</ul>

function tasksController($scope, $location) 
{ 
    $scope.isActive = function (viewLocation) { 
        return viewLocation === $location.path();
    };
}

$scope.getClass = function (path) {
return String(($location.absUrl().split('?')[0]).indexOf(path)) > -1 ? 'active' : ''
}


<li class="listing-head" ng-class="getClass('/v/bookings')"><a href="/v/bookings">MY BOOKING</a></li>
<li class="listing-head" ng-class="getClass('/v/fleets')"><a href="/v/fleets">MY FLEET</a></li>
<li class="listing-head" ng-class="getClass('/v/adddriver')"><a href="/v/adddriver">ADD DRIVER</a></li>
<li class="listing-head" ng-class="getClass('/v/bookings')"><a href="/v/invoice">INVOICE</a></li>
<li class="listing-head" ng-class="getClass('/v/profile')"><a href="/v/profile">MY PROFILE</a></li>
<li class="listing-head"><a href="/v/logout">LOG OUT</a></li>

Я нашел самое простое решение. просто сравнить indexOf в HTML

var myApp = угловой.модуль ('myApp', []);

myApp.run(function($rootScope) {
    $rootScope.$on("$locationChangeStart", function(event, next, current) { 
         $rootScope.isCurrentPath = $location.path();  
    });
});



<li class="{{isCurrentPath.indexOf('help')>-1 ? 'active' : '' }}">
<a href="/#/help/">
          Help
        </a>
</li>