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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/12295983/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-23 07:38:38  来源:igfitidea点击:

Set active tab style with AngularJS

javascripthtmlangularjs

提问by Sergei Basharov

I have routes set in AngularJS like this:

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

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

I have some links on the topbar styled as tabs. How can I add 'active' class to a tab depending on current template or url?

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

回答by Rob Juurlink

A way to solve this without having to rely on URLs is to add a custom attribute to every partial during $routeProviderconfiguration, like this:

一种无需依赖 URL 即可解决此问题的方法是在$routeProvider配置期间为每个部分添加自定义属性,如下所示:

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

Expose $routein your controller:

$route在您的控制器中公开:

function widgetsController($scope, $route) {
    $scope.$route = $route;
}

Set the activeclass based on the current active tab:

active根据当前活动选项卡设置类:

<li ng-class="{active: $route.current.activetab == 'dashboard'}"></li>
<li ng-class="{active: $route.current.activetab == 'lab'}"></li>

回答by pkozlowski.opensource

One way of doing this would be by using ngClass directive and the $location service. In your template you could do:

一种方法是使用 ngClass 指令和 $location 服务。在您的模板中,您可以执行以下操作:

ng-class="{active:isActive('/dashboard')}"

where isActivewould be a function in a scope defined like this:

whereisActive将是一个在如下定义的范围内的函数:

myApp.controller('MyCtrl', function($scope, $location) {
    $scope.isActive = function(route) {
        return route === $location.path();
    }
});

Here is the complete jsFiddle: http://jsfiddle.net/pkozlowski_opensource/KzAfG/

这是完整的 jsFiddle:http: //jsfiddle.net/pkozlowski_opensource/KzAfG/

Repeating ng-class="{active:isActive('/dashboard')}"on each navigation tab might be tedious (if you've got many tabs) so this logic might be a candidate for a very simple directive.

ng-class="{active:isActive('/dashboard')}"在每个导航选项卡上重复可能很乏味(如果您有很多选项卡),因此此逻辑可能是一个非常简单的指令的候选者。

回答by XML

Following Pavel's advice to use a custom directive, here's a version that requires adding no payload to the routeConfig, is super declarative, and can be adapted to react to any level of the path, by simply changing which slice()of it you're paying attention to.

遵循 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");
            }
        });
      }
    };
  });

We're accomplishing our goals by listening for the $routeChangeSuccessevent, rather than by placing a $watchon the path. I labor under the belief that this means the logic should run less often, as I think watches fire on each $digestcycle.

我们通过倾听$routeChangeSuccess事件来实现我们的目标,而不是通过$watch在路径上放置一个。我相信这意味着逻辑应该不那么频繁地运行,因为我认为每个$digest周期都会触发。

Invoke it by passing your path-level argument on the directive declaration. This specifies what chunk of the current $location.path() you want to match your hrefattribute against.

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

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

So, if your tabs should react to the base-level of the path, make the argument '1'. Thus, when location.path() is "/home", it will match against the "#/home" in the href. If you have tabs that should react to the second level, or third, or 11th of the path, adjust accordingly. This slicing from 1 or greater will bypass the nefarious '#' in the href, which will live at index 0.

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

The only requirement is that you invoke on an <a>, as the element is assuming the presence of an hrefattribute, which it will compare to the current path. However, you could adapt fairly easily to read/write a parent or child element, if you preferred to invoke on the <li>or something. I dig this because you can re-use it in many contexts by simply varying the pathLevel argument. If the depth to read from was assumed in the logic, you'd need multiple versions of the directive to use with multiple parts of the navigation.

唯一的要求是您调用<a>,因为该元素假设存在一个href属性,它将与当前路径进行比较。但是,如果您更喜欢调用<li>或 某物,您可以很容易地适应读/写父元素或子元素。我挖掘这个是因为您可以通过简单地改变 pathLevel 参数在许多上下文中重新使用它。如果在逻辑中假设了要读取的深度,则需要多个版本的指令才能与导航的多个部分一起使用。



EDIT 3/18/14: The solution was inadequately generalized, and would activate if you defined an arg for the value of 'activeTab' that returned undefinedagainst both $location.path(), and the element's href. Because: undefined === undefined. Updated to fix that condition.

2014 年 3 月 18 日编辑:该解决方案没有充分概括,如果您为 'activeTab' 的值定义了一个 arg ,该值undefined针对$location.path()和元素的href. 因为:undefined === undefined。更新以修复该情况。

While working on that, I realized there should have been a version you can just declare on a parent element, with a template structure like this:

在处理这个问题时,我意识到应该有一个可以在父元素上声明的版本,模板结构如下:

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

Note that this version no longer remotely resembles Bootstrap-style HTML. But, it's more modern and uses fewer elements, so I'm partial to it. This version of the directive, plus the original, are now available on Githubas a drop-in module you can just declare as a dependency. I'd be happy to Bower-ize them, if anybody actually uses them.

请注意,此版本不再类似于 Bootstrap 样式的 HTML。但是,它更现代,使用的元素更少,所以我偏爱它。这个版本的指令,加上原来的,现在可以在 Github 上作为一个插入模块使用,你可以声明为一个依赖项。如果有人真的使用它们,我很乐意将它们鲍尔化。

Also, if you want a bootstrap-compatible version that includes <li>'s, you can go with the angular-ui-bootstrap Tabs module, which I think came out after this original post, and which is perhaps even more declarative than this one. It's less concise for basic stuff, but provides you with some additional options, like disabled tabs and declarative events that fire on activate and deactivate.

另外,如果你想要一个包含<li>'s的引导兼容版本,你可以使用 angular-ui-bootstrap Tabs 模块,我认为它是在这篇原始文章之后出现的,它可能比这个更具声明性。它对于基本内容不太简洁,但为您提供了一些附加选项,例如禁用选项卡和在激活和停用时触发的声明性事件。

回答by Lucas

@rob-juurlink I improved a bit on your solution:

@rob-juurlink 我对您的解决方案进行了一些改进:

instead of each route needing an active tab; and needing to set the active tab in each controller I do this:

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

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

And the HTML looks like this. The activetab is just the url that relates to that route. This just removes the need to add code in each controller (dragging in dependencies like $route and $rootScope if this is the only reason they're used)

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>

回答by kfis

Maybe a directive like this is might solve your problem: http://jsfiddle.net/p3ZMR/4/

也许这样的指令可能会解决您的问题:http: //jsfiddle.net/p3ZMR/4/

HTML

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

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

    };

}]);

回答by Zymotik

Simplest solution here:

这里最简单的解决方案:

How to set bootstrap navbar active class with Angular JS?

如何使用 Angular JS 设置引导导航栏活动类?

Which is:

这是:

Use ng-controller to run a single controller outside of the ng-view:

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

and include in controllers.js:

并包含在controllers.js中:

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

回答by David Lin

I recommend using the state.ui modulewhich not only support multiple and nested views but also make this kind of work very easy (code below quoted) :

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

Worth reading.

值得一读。

回答by Dave Rapin

Here's another version of XMLillies w/ domi's LI change that uses a search string instead of a path level. I think this is a little more obvious what's happening for my use case.

这是 XMLillies 的另一个版本,带有 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 now looks like:

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>

回答by Michael Falck Wedelg?rd

Bootstrap example.

引导程序示例。

If you are using Angulars built in routing (ngview) this directive can be used:

如果您使用 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);
                }
            });

        }
    };
}]);

Assuming your markup looks like this:

假设您的标记如下所示:

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

I like this was of doing it since you can set the class name you want in your attribute.

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

回答by domi

I found XMLilley's anwser the best and most adaptable and non-intrusive.

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

However I had a small glitch.

但是我有一个小故障。

For use with bootstrap nav, here is how I modified it:

为了与引导程序导航一起使用,这是我修改它的方式:

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

I added "if(attrs.href!=undefined)" because this function is apprently called twice, the second time producing an error.

我添加了“if(attrs.href!=undefined)”,因为这个函数被apprently 调用了两次,第二次产生了错误。

As for the html:

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