Установите активный стиль вкладки с помощью AngularJS
У меня есть маршруты, установленные в AngularJS следующим образом:
$routeProvider
.when('/dashboard', {templateUrl:'partials/dashboard', controller:widgetsController})
.when('/lab', {templateUrl:'partials/lab', controller:widgetsController})
У меня есть некоторые ссылки на верхней панели в стиле вкладок. Как я могу добавить "активный" класс на вкладку в зависимости от текущего шаблона или url?
18 ответов:
способ решить эту проблему без необходимости полагаться на URL-адреса, чтобы добавить пользовательский атрибут для каждого частичного во время
$routeProviderконфигурации, вроде этого:$routeProvider. when('/dashboard', { templateUrl: 'partials/dashboard.html', controller: widgetsController, activetab: 'dashboard' }). when('/lab', { templateUrl: 'partials/lab.html', controller: widgetsController, activetab: 'lab' });разоблачение
$routeв вашем контроллере:function widgetsController($scope, $route) { $scope.$route = $route; }установить
activeкласса на основе текущей активной вкладки:<li ng-class="{active: $route.current.activetab == 'dashboard'}"></li> <li ng-class="{active: $route.current.activetab == 'lab'}"></li>
один из способов сделать это было бы с помощью директивы ngClass и $location service. В вашем шаблоне вы можете сделать:
ng-class="{active:isActive('/dashboard')}"здесь
isActiveбудет функция в области, определенной следующим образом:myApp.controller('MyCtrl', function($scope, $location) { $scope.isActive = function(route) { return route === $location.path(); } });вот полный jsFiddle:http://jsfiddle.net/pkozlowski_opensource/KzAfG/
повторять
ng-class="{active:isActive('/dashboard')}"на каждой вкладке навигации может быть утомительно (если у вас много вкладок) так что эта логика может быть кандидатом для очень простой директива.
следуя совету Павла использовать пользовательскую директиву, вот версия, которая не требует добавления полезной нагрузки в routeConfig, является супер декларативной и может быть адаптирована для реагирования на любой уровень пути, просто изменив который
slice()на это вы обращаете внимание.app.directive('detectActiveTab', function ($location) { return { link: function postLink(scope, element, attrs) { scope.$on("$routeChangeSuccess", function (event, current, previous) { /* Designed for full re-usability at any path, any level, by using data from attrs. Declare like this: <li class="nav_tab"> <a href="#/home" detect-active-tab="1">HOME</a> </li> */ // This var grabs the tab-level off the attribute, or defaults to 1 var pathLevel = attrs.detectActiveTab || 1, // This var finds what the path is at the level specified pathToCheck = $location.path().split('/')[pathLevel] || "current $location.path doesn't reach this level", // This var finds grabs the same level of the href attribute tabLink = attrs.href.split('/')[pathLevel] || "href doesn't include this level"; // Above, we use the logical 'or' operator to provide a default value // in cases where 'undefined' would otherwise be returned. // This prevents cases where undefined===undefined, // possibly causing multiple tabs to be 'active'. // now compare the two: if (pathToCheck === tabLink) { element.addClass("active"); } else { element.removeClass("active"); } }); } }; });мы достигаем наших целей, слушая
$routeChangeSuccessсобытие, а не путем размещения$watchна пути. Я работаю под убеждением, что это означает, что логика должна работать реже, так как я думаю, наблюдает огонь на каждом$digestцикл.вызовите его, передав свой аргумент уровня пути в объявлении директивы. Это указывает, какой кусок текущего $ location.путь() вы хотите, чтобы соответствовать вашим против.
<li class="nav_tab"><a href="#/home" detect-active-tab="1">HOME</a></li>Итак, если ваши вкладки должны реагировать на базовый уровень пути, сделайте аргумент '1'. Таким образом, когда расположение.путь () - это "/ home", он будет соответствовать" #/home " в
href. Если у вас есть вкладки, которые должны реагировать на второй уровень, или третий, или 11-й путь, отрегулируйте соответственно. Эта нарезка от 1 или больше обойдет гнусный ' # ' в href, который будет жить в индексе 0.единственное требование заключается в том, что вы вызываете на
<a>, так как элемент предполагает наличиеhrefатрибут, который он будет сравнивать с текущим путем. Однако вы можете довольно легко адаптироваться к чтению / записи родительского или дочернего элемента, если вы предпочитаете вызывать на<li>или что-то. Я копаю это потому, что вы можете повторно использовать его во многих контекстах, просто изменяя аргумент pathLevel. Если глубина для чтения предполагалась в логике, вам потребуется несколько версий директивы для использования с несколькими частями навигации.
EDIT 3/18/14: решение было неадекватно обобщено и активировалось бы, если бы вы определили arg для значения 'activeTab', которое вернуло
undefinedпротив$location.path()и элементhref. Потому что:undefined === undefined. Обновлено до исправьте это условие.работая над этим, я понял, что должна была быть версия, которую вы можете просто объявить на родительском элементе, со структурой шаблона следующим образом:
<nav id="header_tabs" find-active-tab="1"> <a href="#/home" class="nav_tab">HOME</a> <a href="#/finance" class="nav_tab">Finance</a> <a href="#/hr" class="nav_tab">Human Resources</a> <a href="#/quarterly" class="nav_tab">Quarterly</a> </nav>обратите внимание, что эта версия больше не отдаленно напоминает HTML в стиле Bootstrap. Но он более современный и использует меньше элементов, поэтому я неравнодушен к нему. Эта версия директивы, плюс оригинал, теперь доступно на Github как выпадающий модуль вы можете просто объявить как зависимость. Я бы с удовольствием поклонился им, если кто-то действительно использует их.
кроме того, если вы хотите bootstrap-совместимую версию, которая включает в себя
<li>'s, Вы можете пойти с угловой-ui-bootstrap Tabs модуль, который, я думаю, вышел после этого оригинального сообщения, и который, возможно, даже более декларативен, чем этот. Он менее лаконичен для основных вещей, но предоставляет вам некоторые дополнительные опции, такие как отключенные вкладки и декларативные события, которые срабатывают при активации и деактивировать.
@rob-juurlink я немного улучшил ваше решение:
вместо каждого маршрута нужна активная вкладка; и нужно установить активную вкладку в каждом контроллере я делаю это:
var App = angular.module('App',[]); App.config(['$routeProvider', function($routeProvider){ $routeProvider. when('/dashboard', { templateUrl: 'partials/dashboard.html', controller: Ctrl1 }). when('/lab', { templateUrl: 'partials/lab.html', controller: Ctrl2 }); }]).run(['$rootScope', '$location', function($rootScope, $location){ var path = function() { return $location.path();}; $rootScope.$watch(path, function(newVal, oldVal){ $rootScope.activetab = newVal; }); }]);и HTML выглядит так. Activetab - это только url-адрес, который относится к этому маршруту. Это просто устраняет необходимость добавлять код в каждый контроллер (перетаскивание зависимостей, таких как $route и $rootScope, если это единственная причина, по которой они используются)
<ul> <li ng-class="{active: activetab=='/dashboard'}"> <a href="#/dashboard">dashboard</a> </li> <li ng-class="{active: activetab=='/lab'}"> <a href="#/lab">lab</a> </li> </ul>
возможно, такая директива может решить вашу проблему: http://jsfiddle.net/p3ZMR/4/
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>JS
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 bot return including hashbang scope.location = location; scope.$watch('location.path()', function(newPath) { if (path === newPath) { element.addClass(clazz); } else { element.removeClass(clazz); } }); } }; }]);
самое простое решение здесь:
Как установить bootstrap navbar active class с Angular JS?
что:
используйте ng-контроллер для запуска одного контроллера за пределами ng-view:
<div class="collapse navbar-collapse" ng-controller="HeaderController"> <ul class="nav navbar-nav"> <li ng-class="{ active: isActive('/')}"><a href="/">Home</a></li> <li ng-class="{ active: isActive('/dogs')}"><a href="/dogs">Dogs</a></li> <li ng-class="{ active: isActive('/cats')}"><a href="/cats">Cats</a></li> </ul> </div> <div ng-view></div>и включить в контроллеры.js:
function HeaderController($scope, $location) { $scope.isActive = function (viewLocation) { return viewLocation === $location.path(); }; }
Я рекомендую использовать государство.модуль пользовательского интерфейса которые не только поддерживают несколько и вложенные представления, но и сделать этот вид работы очень легко (код ниже цитируется):
<ul class="nav"> <li ng-class="{ active: $state.includes('contacts') }"><a href="#{{$state.href('contacts')}}">Contacts</a></li> <li ng-class="{ active: $state.includes('about') }"><a href="#{{$state.href('about')}}">About</a></li> </ul>стоит почитать.
вот еще одна версия изменения LI XMLillies w / domi, которая использует строку поиска вместо уровня пути. Я думаю, что это немного более очевидно, что происходит для моего случая использования.
statsApp.directive('activeTab', function ($location) { return { link: function postLink(scope, element, attrs) { scope.$on("$routeChangeSuccess", function (event, current, previous) { if (attrs.href!=undefined) { // this directive is called twice for some reason // The activeTab attribute should contain a path search string to match on. // I.e. <li><a href="#/nested/section1/partial" activeTab="/section1">First Partial</a></li> if ($location.path().indexOf(attrs.activeTab) >= 0) { element.parent().addClass("active");//parent to get the <li> } else { element.parent().removeClass("active"); } } }); } }; });HTML теперь выглядит так:
<ul class="nav nav-tabs"> <li><a href="#/news" active-tab="/news">News</a></li> <li><a href="#/some/nested/photos/rawr" active-tab="/photos">Photos</a></li> <li><a href="#/contact" active-tab="/contact">Contact</a></li> </ul>
Я нашел anwser XMLilley самым лучшим и самым адаптируемым и ненавязчивым.
однако у меня был небольшой глюк.
для использования с bootstrap nav, вот как я изменил его:
app.directive('activeTab', function ($location) { return { link: function postLink(scope, element, attrs) { scope.$on("$routeChangeSuccess", function (event, current, previous) { /* designed for full re-usability at any path, any level, by using data from attrs declare like this: <li class="nav_tab"><a href="#/home" active-tab="1">HOME</a></li> */ if(attrs.href!=undefined){// this directive is called twice for some reason // this var grabs the tab-level off the attribute, or defaults to 1 var pathLevel = attrs.activeTab || 1, // this var finds what the path is at the level specified pathToCheck = $location.path().split('/')[pathLevel], // this var finds grabs the same level of the href attribute tabLink = attrs.href.split('/')[pathLevel]; // now compare the two: if (pathToCheck === tabLink) { element.parent().addClass("active");//parent to get the <li> } else { element.parent().removeClass("active"); } } }); } }; });я добавил " if (attrs.хреф!=undefined) " потому что эта функция вызывается дважды, во второй раз создавая ошибку.
Что касается html:
<ul class="nav nav-tabs"> <li class="active" active-tab="1"><a href="#/accueil" active-tab="1">Accueil</a></li> <li><a active-tab="1" href="#/news">News</a></li> <li><a active-tab="1" href="#/photos" >Photos</a></li> <li><a active-tab="1" href="#/contact">Contact</a></li> </ul>
пример начальной загрузки.
Если вы используете Ангуляры, встроенные в маршрутизацию (ngview), эта директива может быть использована:
angular.module('myApp').directive('classOnActiveLink', [function() { return { link: function(scope, element, attrs) { var anchorLink = element.children()[0].getAttribute('ng-href') || element.children()[0].getAttribute('href'); anchorLink = anchorLink.replace(/^#/, ''); scope.$on("$routeChangeSuccess", function (event, current) { if (current.$$route.originalPath == anchorLink) { element.addClass(attrs.classOnActiveLink); } else { element.removeClass(attrs.classOnActiveLink); } }); } }; }]);предполагая, что ваша разметка выглядит так:
<ul class="nav navbar-nav"> <li class-on-active-link="active"><a href="/orders">Orders</a></li> <li class-on-active-link="active"><a href="/distributors">Distributors</a></li> </ul>мне нравится, что это было делать, так как вы можете установить имя класса, которое вы хотите в свой атрибут.
можно просто введите местоположение в область и использовать это, чтобы вычесть стиль навигация:
function IndexController( $scope, $rootScope, $location ) { $rootScope.location = $location; ... }тогда используйте его в своем
ng-class:<li ng-class="{active: location.path() == '/search'}"> <a href="/search">Search><a/> </li>
альтернативный способ-использовать ui-sref-active
директива, работающая вместе с ui-sref для добавления классов к элементу, когда состояние связанной директивы ui-sref активно, и удаления их, когда оно неактивно. Основной вариант использования-упростить специальный внешний вид меню навигации, опираясь на ui-sref, имея кнопку меню "активного" состояния, которая отличается от неактивного меню предметы.
использование:
интерфейс-сref-активный='класс1 класс2 класс_3' - класса "класс1", "класс2", и "класс_3" каждое добавленное к директиве элемента, когда элементы интерфейса, относящиеся к-сref состояние активное, и удаляют, когда он не активен.
пример:
Учитывая следующий шаблон,<ul> <li ui-sref-active="active" class="item"> <a href ui-sref="app.user({user: 'bilbobaggins'})">@bilbobaggins</a> </li> <!-- ... --> </ul>когда состояние приложения "приложение.user", и содержит параметр состояния "user" со значением "bilbobaggins", в результате чего появится HTML как
<ul> <li ui-sref-active="active" class="item active"> <a ui-sref="app.user({user: 'bilbobaggins'})" href="/users/bilbobaggins">@bilbobaggins</a> </li> <!-- ... --> </ul>имя класса интерполируется один раз во время ссылки директив (любые дальнейшие изменения интерполированного значения игнорируются). Несколько классов могут быть указаны через пробел формат.
используйте директиву ui-sref-opts для передачи параметров в состояние$.идти.)( Пример:
<a ui-sref="home" ui-sref-opts="{reload: true}">Home</a>
Я согласен с сообщением Роба о наличии пользовательского атрибута в контроллере. Видимо, у меня недостаточно репутации, чтобы комментировать. Вот jsfiddle, который был запрошен:
пример html
<div ng-controller="MyCtrl"> <ul> <li ng-repeat="link in links" ng-class="{active: $route.current.activeNav == link.type}"> <a href="{{link.uri}}">{{link.name}}</a> </li> </ul> </div>примера приложения.js
angular.module('MyApp', []).config(['$routeProvider', function ($routeProvider) { $routeProvider.when('/a', { activeNav: 'a' }) .when('/a/:id', { activeNav: 'a' }) .when('/b', { activeNav: 'b' }) .when('/c', { activeNav: 'c' }); }]) .controller('MyCtrl', function ($scope, $route) { $scope.$route = $route; $scope.links = [{ uri: '#/a', name: 'A', type: 'a' }, { uri: '#/b', name: 'B', type: 'b' }, { uri: '#/c', name: 'C', type: 'c' }, { uri: '#/a/detail', name: 'A Detail', type: 'a' }]; });
'use strict'; angular.module('cloudApp') .controller('MenuController', function ($scope, $location, CloudAuth) { $scope.menu = [ { 'title': 'Dashboard', 'iconClass': 'fa fa-dashboard', 'link': '/dashboard', 'active': true }, { 'title': 'Devices', 'iconClass': 'fa fa-star', 'link': '/devices' }, { 'title': 'Settings', 'iconClass': 'fa fa-gears', 'link': '/settings' } ]; $location.path('/dashboard'); $scope.isLoggedIn = CloudAuth.isLoggedIn; $scope.isAdmin = CloudAuth.isAdmin; $scope.isActive = function(route) { return route === $location.path(); }; });и использовать ниже в шаблоне:
<li role="presentation" ng-class="{active:isActive(menuItem.link)}" ng-repeat="menuItem in menu"><a href="{{menuItem.link}}"><i class="{{menuItem.iconClass}}"></i> {{menuItem.title}}</a></li>
мне нужно было решение, которое не требует изменений контроллеров, потому что для некоторых страниц мы только визуализируем шаблоны, и нет никакого контроллера вообще. Спасибо предыдущим комментаторам, которые предложили использовать
$routeChangeSuccessЯ придумал что-то вроде этого:# Directive angular.module('myapp.directives') .directive 'ActiveTab', ($route) -> restrict: 'A' link: (scope, element, attrs) -> klass = "active" if $route.current.activeTab? and attrs.flActiveLink is $route.current.activeTab element.addClass(klass) scope.$on '$routeChangeSuccess', (event, current) -> if current.activeTab? and attrs.flActiveLink is current.activeTab element.addClass(klass) else element.removeClass(klass) # Routing $routeProvider .when "/page", templateUrl: "page.html" activeTab: "page" .when "/other_page", templateUrl: "other_page.html" controller: "OtherPageCtrl" activeTab: "other_page" # View (.jade) a(ng-href='/page', active-tab='page') Page a(ng-href='/other_page', active-tab='other_page') Other pageэто не зависит от URL-адресов, и поэтому его очень легко настроить для любых подстраниц и т. д.
Я не могу вспомнить, где я нашел этот метод, но он довольно прост и хорошо работает.
HTML:
<nav role="navigation"> <ul> <li ui-sref-active="selected" class="inactive"><a ui-sref="tab-01">Tab 01</a></li> <li ui-sref-active="selected" class="inactive"><a ui-sref="tab-02">Tab 02</a></li> </ul> </nav>CSS:
.selected { background-color: $white; color: $light-blue; text-decoration: none; border-color: $light-grey; }
если вы используете ngRoute (для маршрутизации), то ваше приложение будет иметь конфигурацию ниже,
angular .module('appApp', [ 'ngRoute' ]) config(function ($routeProvider) { $routeProvider .when('/', { templateUrl: 'views/main.html', controller: 'MainCtrl', controllerAs: 'main' }) .when('/about', { templateUrl: 'views/about.html', controller: 'AboutCtrl', controllerAs: 'about' }) } });теперь, просто добавьте контроллер в этой конфигурации, как показано ниже,
angular .module('appApp', [ 'ngRoute' ]) config(function ($routeProvider) { $routeProvider .when('/', { templateUrl: 'views/main.html', controller: 'MainCtrl', activetab: 'main' }) .when('/about', { templateUrl: 'views/about.html', controller: 'AboutCtrl', activetab: 'about' }) } }) .controller('navController', function ($scope, $route) { $scope.$route = $route; });как вы упомянули активную вкладку в вашей конфигурации, теперь вам просто нужно добавить активный класс в свой
<li>или<a>тег. Мол,ng-class="{active: $route.current.activetab == 'about'}"что означает, что всякий раз, когда пользователь нажимает на страницу "о программе", это автоматически идентифицирует текущую вкладку и применяет активный css класс.
надеюсь, это поможет!
пришел сюда за решением .. хотя выше решения работают нормально, но нашли их немного сложными ненужными. Для людей, которые все еще ищут простое и аккуратное решение, он отлично справится с задачей.
<section ng-init="tab=1"> <ul class="nav nav-tabs"> <li ng-class="{active: tab == 1}"><a ng-click="tab=1" href="#showitem">View Inventory</a></li> <li ng-class="{active: tab == 2}"><a ng-click="tab=2" href="#additem">Add new item</a></li> <li ng-class="{active: tab == 3}"><a ng-click="tab=3" href="#solditem">Sold item</a></li> </ul> </section>
Comments