用AngularJS设置活动标签样式

我有这样的AngularJS路线设置:

$routeProvider .when('/dashboard', {templateUrl:'partials/dashboard', controller:widgetsController}) .when('/lab', {templateUrl:'partials/lab', controller:widgetsController}) 

我在顶栏上有一些链接样式为选项卡。 如何添加“活动”类到一个标签取决于当前模板或url?

解决这个问题的方法不需要依赖于URL,而是在$routeProviderconfiguration期间$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服务。 在你的模板中,你可以这样做:

 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')}"可能很乏味(如果你有很多选项卡),所以这个逻辑可能是一个非常简单的指令的候选人。

遵循Pavel的build议使用自定义指令,这里是一个版本,要求不添加任何有效载荷到routeConfig,是超级说明性的,并且可以适应对任何级别的path作出反应,只需更改它的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事件来完成我们的目标,而不是在path上放置$watch 。 我认为这意味着逻辑运行的次数会减less,因为我认为每个$digest循环都会触发手表。

通过在指令声明上传递path级参数来调用它。 这指定了要匹配你的href属性的当前$ location.path()块。

 <li class="nav_tab"><a href="#/home" detect-active-tab="1">HOME</a></li> 

因此,如果您的选项卡应对path的基本级别做出反应,请将参数设置为“1”。 因此,当location.path()是“/ home”时,它将匹配href中的“#/ home”。 如果您有对第二层,第三层或第十一层path有反应的标签,请相应地进行调整。 这个从1或更大的切片将绕过href中的恶毒的'#',它将在索引0处生存。

唯一的要求是你在<a>上调用,因为元素假定存在一个href属性,它将和当前path进行比较。 然而,如果你喜欢在<li>上调用,你可以很容易地适应读/写父或子元素。 我挖掘这个是因为你可以通过简单地改变pathLevel参数在很多情况下重用它。 如果在逻辑中假定要读取的深度,则需要多个版本的指令以与导航的多个部分一起使用。


编辑3/18/14:解决scheme是不充分的概括,并且将激活,如果你定义了一个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> 

请注意,此版本不再类似于Bootstrap风格的HTML。 但是,它更现代,使用更less的元素,所以我偏爱它。 这个版本的指令,加上原来的,现在可以在Github上作为一个插件模块,你可以声明为依赖。 如果有人真的使用它们,我会很乐意把它们捆绑起来。

另外,如果你需要一个包含<li>的bootstrap兼容版本,你可以使用angular-ui-bootstrap Tabs模块 ,这个模块我认为是在原始文章后面出现的,甚至可能比这个更具说明性一。 对于基本的东西来说,这不太简洁,但为您提供了一些额外的选项,例如禁用的选项卡和声明性事件,在激活和停用时触发。

@ rob-juurlink我在你的解决scheme上有所改进:

而不是每个路线需要一个活动选项卡; 并需要设置每个控制器中的活动选项卡我这样做:

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

最简单的解决scheme:

如何设置与Angular JS引导的navbar活动类?

这是:

使用ng-controller在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> 

并包含在controllers.js中:

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

我推荐使用state.ui模块 ,它不仅支持多重和嵌套的视图,而且使这种工作变得非常简单(下面引用的代码):

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

值得一读。

这是XMLillies w / domi的LI更改的另一个版本,它使用searchstring而不是path级别。 我认为这是更明显的是我的用例发生了什么。

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

我发现XMLilley的最好,最适应性和非侵入性。

不过,我有一个小故障。

为了使用自举导航,这里是我如何修改它:

 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.href!= 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> 

Bootstrap例子。

如果你正在使用路由(ngview)构build的Angulars,可以使用这个指令:

 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> 

我喜欢这样做,因为你可以在你的属性中设置你想要的类名。

您也可以简单地将该位置注入到范围中,并使用该位置来减less导航的样式:

 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指令的状态处于活动状态时向元素添加类,并在非活动状态时移除它们。 主要用途是通过使“活动”状态的菜单button显示不同,将其与非活动菜单项区分开来,以简化依赖于ui-sref的导航菜单的特殊外观。

用法:

ui-sref-active ='class1 class2 class3' – 类“class1”,“class2”和“class3”分别在相关ui-sref状态为活动状态时添加到指令元素,并在非活动状态时移除。

例:
鉴于以下模板,

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

当应用程序状态为“app.user”,并且包含值为“bilbobaggins”的状态参数“user”时,生成的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指令将选项传递给$ state.go()。 例:

 <a ui-sref="home" ui-sref-opts="{reload: true}">Home</a> 

我同意Rob关于在控制器中具有自定义属性的文章。 显然我没有足够的代表评论。 这是请求的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> 

示例app.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' }]; }); 

http://jsfiddle.net/HrdR6/

 '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>&nbsp;&nbsp;{{menuItem.title}}</a></li> 

我需要一个不需要对控制器进行更改的解决scheme,因为对于某些页面,我们只能渲染模板,根本没有控制器。 感谢以前的评论者提出使用$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,因此它很容易设置为任何子网页等

我不记得我在哪里find这个方法,但是它非常简单并且运行良好。

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

来到这里解决scheme..虽然上述解决scheme工作正常,但发现他们有点复杂不必要的。 对于那些仍然在寻求一个简单而整洁的解决scheme的人来说,它将完美地完成任务。

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