使用AngularJS设置活动标签样式


144

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

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

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



4
@AminMeyghani这个问题怎么可能是一年问到的重复问题?
摄政王

Answers:


274

解决此问题而不必依赖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>

3
这是到目前为止我所见过的最好的解决方案,因为它支持动态网址,例如/ foo /:bar。
martinpaulucci 2012年

3
我实际上还无法使它正常工作。您能提供plnkr吗?
PPPaul

9
只是一件事:更好地进行设置,$scope.activeTab = $route.current.activetab以便可以使html保持更清洁。
Christoph

2
这在AngularJS 1.0.8中不起作用。$ route.current未定义。
fish鱼

2
将此与@Lucas的$rootScope技巧结合使用,以使其在所有范围内可用。
colllin 2014年

134

一种方法是使用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')}"在每个导航选项卡上重复操作可能很乏味(如果您有很多选项卡),因此此逻辑可能是非常简单的指令的候选对象。


1
我花了很长时间才发现“非常简单的指令”实际上非常简单,因此在下面提供了一个。:-)它应该在各种上下文中都可以重用,并且没有非声明性配置。
XML

1
查看jsFiddle,如何在页面加载时将当前页面设置为活动状态?该示例仅在用户单击选项时有效。例如,当您从外部链接登陆到首页时,您可能希望突出显示“主页”导航。
thathurtabit

啊哈为此挠了一下头。谢谢!
masterwok

41

遵循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周期都会触发。

通过在指令声明中传递路径级别的参数来调用它。这指定要与href属性匹配的当前$ location.path()中的哪个块。

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

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

唯一的要求是您在上调用<a>,因为该元素假定存在href属性,该属性将与当前路径进行比较。但是,如果您喜欢在<li>或上进行调用,则可以轻松地进行调整以读取/写入父元素或子元素。我之所以这样做是因为您可以通过简单地更改pathLevel参数在许多情况下重用它。如果逻辑中假定要读取的深度,则您需要该指令的多个版本才能与导航的多个部分一起使用。


编辑3/18/14:解决方案的泛化不充分,如果您为undefined针对$location.path()和元素的返回的'activeTab'的值定义了一个arg,则该解决方案将被激活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上作为嵌入式模块使用,您可以将其声明为依赖项。如果有人真正使用它们,我很乐意对其进行Bower化处理。

另外,如果您想要一个包含<li>的兼容引导程序的版本,则可以使用 angular-ui-bootstrap Tabs模块,我认为它是在本原始帖子之后发布的,它可能比本声明更具声明性。对于基本内容来说不够简洁,但是为您提供了其他一些选项,例如禁用的选项卡以及在激活和停用时触发的声明性事件。


4
我简直不敢相信没有人对此投一票!这是我的2美分。尽管代码中有一个小错误,但我认为'tabLevel'应该是'activeTab'。对于Bootstrap样式,您可能希望将'active'类添加到LI元素而不是A元素。但这仅需要进行较小的更改。
David Lin

1
您对activeTab @DavidLin绝对正确。编辑。但是,我不喜欢Bootstrap的结构,因此存在故意的差异。实际上,我开始认为nav抽象可能根本不属于ul,而可能应该只是由a nav或其他分组元素包裹的锚点集合。处理的中间层li会增加复杂性,而且不会带来任何收益,尤其是在我们已经掌握nav了处理现成问题的要素之后,尤其如此。
XML

这是简单而辉煌的。我很惊讶Angular中没有这样的东西可以检查您所走的路线。
Intellix

1
要使其与bootstrap3一起使用,您要做的就是将element.addClass(“ active”);更改为element.parent('li')。addClass(“ active”);`我认为这可能会更好虽然命名,类似的主动标签的INSEAD 有源标签,这似乎宣告的标签被激活。否则,这是一个非常好的指令。在@domi的答案中看到此更改
boatcoder 2013年

此页面上的最佳解决方案,简直无法相信它的赞誉。
Karolis 2014年

27

@ 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只是与该路由相关的网址。这只是消除了在每个控制器中添加代码的需要(如果这是使用它们的唯一原因,则可以拖动$ 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>

非常感谢您的修改。非常好。您对在页面首次加载时设置活动标签有任何建议吗?
Hairgami_Master 2013年

2
取决于您想要的。通常,您会将'/'url作为您的主控制器。这样,当用户转到您的网址时,它将加载该控制器并将该选项卡设置为活动选项卡。在上面的示例中,我没有'/'网址,因此,如果您的情况如此,只需添加.ot​​herwise()$ routeProvider。when('/ dashboard',{templateUrl:'partials / dashboard.html',控制器:Ctrl1})。when('/ lab',{templateUrl:'partials / lab.html',控制器:Ctrl2})。否则({redirectTo:'/ dashboard'}); 祝你好运!
卢卡斯

非常感谢@Lucas。有帮助。由于某些原因,我不得不在主路由中添加#符号-when('#/',{controller:FormsController,templateUrl:'partials / dashboard.html'})。
Hairgami_Master 2013年

我更喜欢这种方式。拥有rootScope并可以在任何地方执行任何操作
wrivas

16

也许像这样的指令可能会解决您的问题: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);
                }
            });
        }

    };

}]);

1
请注意,如果href包含以下表达式,则必须使用$ observe:docs.angularjs.org/guide/directive#Attributes。请参阅更新的提琴:jsfiddle.net/p3ZMR/10
Narretz,2012年

14

这里最简单的解决方案:

如何使用Angular JS设置bootstrap 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();
    };
}

2
同意,到目前为止最简单的
做法

12

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

值得读。


4

这是带有domi的LI更改的XMLillies的另一个版本,该版本使用搜索字符串而不是路径级别。我认为这对于我的用例来说更加明显。

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>

3

我发现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>

nvm,这是我的错,它被两次叫过。我猜不需要“ if(attrs.href!= undefined)”。
domi

3

引导程序示例。

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

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


2

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

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

然后在您的ng-class

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

标记中应该不是$ root.location.path()吗?
Irshu 2014年

@Irshu:可能更干净,但是上述方法对我也有效。
Der Hochstapler 2014年

2

另一种方法是使用ui-sref-active

指令与ui-sref一起工作,以在相关ui-sref指令的状态为活动时将类添加到元素,并在不活动时将其删除。主要用例是通过使“活动”状态的菜单按钮看起来不同,从而将其与非活动菜单项区分开来简化依赖于ui-sref的导航菜单的特殊外观。

用法:

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

示例:
给定以下模板,

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

当应用程序状态为“ app.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指令将选项传递给$ state.go()。例:

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

谢谢。在离子框架中工作时真的很有用!
阿维吉特古普塔(Avijit Gupta)

1

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


我喜欢链接列表的数据驱动方法。而且,有些人可能选择将链接数组移到服务/工厂中。
格兰特·林赛

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

0

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


0

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

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

0

如果您使用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类。

我希望这有帮助!


-3

来到这里寻求解决方案..尽管上述解决方案可以正常工作,但是发现它们有点复杂,没有必要。对于仍在寻找简单易用的解决方案的人们,它将完美地完成任务。

<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>
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.