使用 AngularJS 设置活动选项卡样式

我在 AngularJS 中设置了这样的路线:

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

我在顶栏上有一些标签样式的链接。如何根据当前模板或 URL 将“活动”类添加到选项卡中?

177942 次浏览

一种方法是使用 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')}"可能很乏味(如果有很多选项卡的话) ,因此这种逻辑可能是一个非常简单的指令的候选者。

解决这个问题的一个方法是在 $routeProvider配置期间为每个部分添加一个自定义属性,而不必依赖 URL,如下所示:

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

也许这样的指令可以解决你的问题: Http://jsfiddle.net/p3zmr/4/

超文本标示语言

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


};


}]);

你也可以简单地使用 将位置注入到瞄准镜中来演绎导航的风格:

function IndexController( $scope, $rootScope, $location ) {
$rootScope.location = $location;
...
}

然后在你的 ng-class中使用它:

<li ng-class="{active: location.path() == '/search'}">
<a href="/search">Search><a/>
</li>

我同意 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/

@ 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。这只是消除了在每个控制器中添加代码的需要(如果这是使用 $path 和 $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>

根据 Pavel 关于使用自定义指令的建议,这里有一个版本,它不需要向 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.path ()中希望与 href属性匹配的块。

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

因此,如果您的选项卡应该对路径的基本级别做出反应,那么将参数设置为“1”。因此,当 location.path ()是“/home”时,它将与 href中的“ #/home”匹配。如果有应该对路径的第二级、第三级或第11级作出反应的选项卡,则相应地进行调整。这个从1或更大的切片将绕过 href 中的邪恶的“ #”,该“ #”位于索引0处。

唯一的要求是对 <a>进行调用,因为元素假设存在 href属性,并将其与当前路径进行比较。但是,如果您更喜欢对 <li>或其他元素进行调用,则可以相当容易地对父元素或子元素进行读/写调用。我之所以挖掘这一点,是因为您可以在许多上下文中通过简单地更改 path Level 参数来重用它。如果在逻辑中假定要读取的深度,则需要指令的多个版本来与导航的多个部分一起使用。


编辑3/18/14: 这个解决方案没有被充分地推广,如果您为返回 undefined的‘ activeTab’和元素的 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 不再有很大的相似性。但是,它更现代,使用更少的元素,所以我偏爱它。这个版本的指令,加上原来的,现在是 可以在 Github 上找到作为一个下拉模块,您可以声明为一个依赖项。如果真有人用的话,我很乐意把它们改成鲍尔式的。

此外,如果你想要一个引导兼容的版本,其中包括 <li>的,你可以去与 角度自举标签模块,我认为是在这个原始的帖子之后出来,这可能是更声明性比这一个。对于基本的东西,它不那么简洁,但是提供了一些额外的选项,比如禁用的选项卡和激活和停用时触发的声明性事件。

我建议使用 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>

值得一读。

我发现 XMLilley 的答案是最好的、适应性最强的、非侵入性的。

然而,我有一个小故障。

对于使用 bootstrap 导航,以下是我如何修改它:

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! = unDefinition)”,因为这个函数被学习调用两次,第二次会产生一个错误。

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

下面是 XMLillies w/domi 的 LI 更改的另一个版本,它使用搜索字符串而不是路径级别。我认为在我的用例中发生的情况更加明显。

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>

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

这里最简单的解决方案是:

如何设置自举导航栏活动类与角 JS?

那就是:

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

并包含在 controller s.js 中:

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

引导程序示例。

如果你使用的是内置的角路由(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>

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

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

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

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

另一种方法是使用 自动激活

与 ui-sref 一起工作的指令,在相关 ui-sref 指令的状态处于活动状态时向元素添加类,在不活动状态时删除类。主要的用例是简化依赖 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 状态为“ 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>

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

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

希望这个能帮上忙!