Как выделить текущий пункт меню?
AngularJS помогает каким-либо образом с настройкой active
класс по ссылке для текущей страницы?
Я предполагаю, что есть какой-то волшебный способ сделать это, но я не могу найти.
Мое меню выглядит так:
<ul>
<li><a class="active" href="/tasks">Tasks</a>
<li><a href="/actions">Tasks</a>
</ul>
и у меня есть контроллеры для каждого из них в моих маршрутах: TasksController
а также ActionsController
,
Но я не могу найти способ связать "активный" класс на a
ссылки на контроллеры.
Есть намеки?
30 ответов
На вид
<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; }
Вот простой подход, который хорошо работает с 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;
};
Эта тема имеет ряд других похожих ответов.
Как настроить загрузочный активный класс navbar с Angular JS?
Просто чтобы добавить два цента в дебаты, я сделал чисто угловой модуль (без 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/
В моем случае я решил эту проблему, создав простой контроллер, отвечающий за навигацию
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>
Для пользователей AngularUI Router:
<a ui-sref-active="active" ui-sref="app">
И это поместит active
класс на объекте, который выбран.
E сть ng-class
директива, которая связывает переменную и класс CSS. Он также принимает объект (className против пар значения bool).
Вот пример, http://plnkr.co/edit/SWZAqj
Ответ @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>
Вот решение, которое я придумал после прочтения некоторых из превосходных предложений выше. В моей конкретной ситуации я пытался использовать компонент вкладок Bootstrap в качестве моего меню, но не хотел использовать его версию Angular-UI, потому что я хочу, чтобы вкладки действовали как меню, где каждая вкладка имеет возможность закладки, а не вкладки, выступающие в качестве навигации для одной страницы. (См. http://angular-ui.github.io/bootstrap/, если вам интересно, как выглядит версия загрузочных вкладок 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>
Вот плункер для этого: 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" : "";
};
});
Использование 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
Использовать директиву angular-ui-router ui-sref-active https://github.com/angular-ui/ui-router/wiki/Quick-Reference
<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 перед угловым включением будет выглядеть так:
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};
});
Мое решение этой проблемы, используйте route.current
в угловом шаблоне.
Как у вас есть /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>
На мой взгляд, это намного чище, чем все предлагаемые решения.
Если вам нужны ссылки для директивы в оболочке, а не для выбора каждой отдельной ссылки (упрощает просмотр области действия в 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 "полный жир", но вы можете легко изменить то, что я сделал с фильтрацией и так далее.
Для тех, кто использует 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>
Вот расширение директивы 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>
Эта директива будет соответствовать уровню глубины, указанному в значении атрибута для директивы. Просто означает, что это может быть использовано в другом месте много раз.
Ни одно из вышеперечисленных предложений не было для меня полезным. Если у вас есть загрузчик 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>
Вот мои два цента, это прекрасно работает.
ПРИМЕЧАНИЕ. Это не соответствует дочерним страницам (что мне и нужно).
Посмотреть:
<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()
}
Согласно ответу @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; }
Вот еще одна директива для выделения активных ссылок.
Ключевая особенность:
- Прекрасно работает с href, который содержит динамические угловые выражения
- Совместим с хэш-бэнг навигацией
- Совместим с Bootstrap, где активный класс должен применяться к родителю, а не к самой ссылке
- Позволяет сделать ссылку активной, если активен какой-либо вложенный путь
- Позволяет сделать ссылку отключенной, если она не активна
Код:
.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, тогда ссылка будет активной, если location это / url / 2:
<a href="#!/url/{{var}}" active-link>
Пример начальной загрузки, родительский ли получит активный класс:
<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>
Пример с отключенной ссылкой, если она не активна, у нее будет класс "отключен":
<a href="#!/url" active-link active-link-disabled>
Все атрибуты active-link-* могут использоваться в любой комбинации, поэтому могут быть реализованы очень сложные условия.
Я нашел самое простое решение. просто сравнить 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>
Самым важным для меня было совсем не менять код начальной загрузки. Вот мой контроллер меню, который ищет пункты меню и затем добавляет желаемое поведение.
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>
Вот гораздо лучший способ сделать это
<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();
};
}
Маршрут:
$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();
...
Проблема, которую я обнаружил здесь, заключается в том, что пункт меню активен только при полной загрузке страницы. При частичном просмотре меню не меняется. Кто-нибудь знает, почему это происходит?
$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>
Я просто написал директиву для этого.
Использование:
<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();
});
});
});
Была такая же проблема. Вот мое решение:
.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'
])