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

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

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

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

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

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

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

Любые подсказки?

Ответ 1

в представлении

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

на контроллере

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

С этим ссылка на задания будет иметь активный класс в любом URL-адресе, который начинается с '/tasks'(e.g.'/tasks/1/reports ')

Ответ 2

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

Но еще не совершенен. Следите за хэш-бэгами;)

Вот директива 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; }

Ответ 3

Вот простой подход, который хорошо работает с Angular.

<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;
};

В этой ветке есть несколько других подобных ответов.

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

Ответ 4

Просто чтобы добавить свои два цента в дебаты, я создал чистый модуль angular (без jQuery), и он также будет работать с хэш-адресами, содержащими данные. (например, #/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/

Ответ 5

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

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>

Ответ 6

Для AngularUI Пользователи маршрутизатора:

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

И это поместит класс active на выбранный объект.

Ответ 7

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

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

Ответ 8

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

Итак, я немного улучшил его, вот код. Я работаю с Bootstrap, поэтому активная часть находится в элементе <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>

Ответ 9

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

Мне очень понравился ответ kfis о создании собственной директивы, чтобы справиться с этим, однако было очень сложно иметь директиву, которая должна была быть размещена на каждой отдельной ссылке. Поэтому я создал свою собственную директиву Angular, которая помещается вместо нее один раз на 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>

Здесь plunker для него: http://plnkr.co/edit/xwGtGqrT7kWoCKnGDHYN?p=preview.

Ответ 10

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

<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" : "";
  };
});

Ответ 12

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

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>

Ответ 13

Использование Angular версии 6 с Bootstrap 4.1

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

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

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

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

Узнайте больше на сайте Angular

Ответ 14

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

$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>

Ответ 15

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

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, будет выглядеть следующим образом:

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

Ответ 16

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

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

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

Затем в вашем шаблоне tasks.html вы можете использовать следующую директиву ng-class:

<a href="app.html#/tasks" 
    ng-class="{active : route.current.menuItem === 'TASKS'}">Tasks</a>

По-моему, это намного чище, чем все предлагаемые решения.

Ответ 17

Вот расширение на директиве 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>

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

Ответ 18

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

Основные возможности:

  • Работает отлично с href, содержащим динамические выражения angular
  • Совместимость с навигацией по хешированию
  • Совместимость с 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);
                }
            );
        }
    };
}
]);

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

Простой пример с выражением angular, скажем $scope.var = 2, тогда ссылка будет активна, если location/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>

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

Ответ 19

Если вам нужны ссылки для директивы в оболочке, а не выбор каждой отдельной ссылки (упрощает просмотр области в 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, но вы можете легко изменить то, что я сделал с фильтрацией и т.д.

Ответ 20

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

ПРИМЕЧАНИЕ. Это не соответствует дочерним страницам (это то, что мне нужно).

Вид:

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

Контроллер:

// make sure you inject $location as a dependency

$scope.isCurrentLocation = function(path){
    return path === $location.path()
}

Ответ 21

В соответствии с ответом @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; }

Ответ 22

Для пользователей, использующих 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>

Ответ 23

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

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

(это может быть запуск $ yo angular), тогда вы хотите добавить .active в родительский класс классов <li>, а не сам элемент; я <li class="active">..</li>. Поэтому я написал следующее:

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

Ответ 24

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

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>

Ответ 25

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

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

Ответ 26

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

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

<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 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 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();
    });
  });
});

Ответ 27

Маршрут:

$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();
...

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

Ответ 28

$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>

Ответ 29

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

var myApp = angular.module('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>

Ответ 30

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

<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();
    };
}