web-dev-qa-db-ja.com

AngularJSでアクティブなタブスタイルを設定する

私はこのようにAngularJSで設定されたルートを持っています:

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

トップバーにはタブ付きのリンクがいくつかあります。現在のテンプレートまたはURLに応じてタブに「アクティブ」クラスを追加する方法を教えてください。

144
Sergei Basharov

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>
274
Rob Juurlink

これを行う1つの方法は、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')}"を繰り返すのは面倒です(たくさんのタブがある場合)ので、このロジックは非常に単純なディレクティブの候補になるかもしれません。

133

カスタムディレクティブを使用するという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"と一致します。パスの2番目のレベル、3番目、または11番目に反応するタブがある場合は、それに応じて調整します。 1以上のこのスライスは、インデックス0にある、href内の不正な「#」をバイパスします。

唯一の要件は、要素が現在のパスと比較されるhref属性の存在を想定しているため、<a>に対して呼び出すことです。ただし、<li>などで呼び出すことを好む場合は、親要素または子要素の読み取り/書き込みにかなり簡単に適応できます。これは、pathLevel引数を変更するだけで多くのコンテキストで再利用できるためです。読み取る深さがロジックで想定されている場合は、ナビゲーションの複数の部分で使用するディレクティブの複数のバージョンが必要になります。


編集3/18/14:解決策が不適切に一般化されていて、$location.path()と要素のundefinedの両方に対してhrefを返す 'activeTab'の値に引数を定義した場合に有効になります。理由は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に似ていないことに注意してください。しかし、それはより現代的であり、使用する要素が少ないので、私は部分的です。このバージョンのディレクティブとオリジナルのバージョンは、 Githubで利用可能 ドロップインモジュールとして使用できるようになりました。依存関係として宣言することができます。誰かが実際にそれらを使用するのであれば、私はそれらをBower -izeすることを嬉しく思います。

また、<li>を含むブートストラップ互換のバージョンが必要な場合は、 angular-ui-bootstrap Tabsモジュール を使用することができます。これは、この最初の投稿の後に出てきたと考えられます。これより宣言的。基本的なものについてはそれほど簡潔ではありませんが、無効化されたタブやアクティブ化と非アクティブ化の際に発生する宣言型イベントなど、いくつかの追加オプションがあります。

41
XML

@ 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はこんな感じです。アクティブタブは、そのルートに関連する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>
27
Lucas

多分このような指令はあなたの問題を解決するかもしれません: 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);
                }
            });
        }

    };

}]);
16
kfis

ここで最も簡単な解決策:

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

そしてcontrollers.jsにインクルードします。

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

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>

読む価値があります。

12
David Lin

これは、パスレベルではなく検索文字列を使用する、XMLIrmiesの別のバージョンです。私のユースケースで起こっていることはもう少し明白だと思います。

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>
4
Dave Rapin

私は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)"を追加したのは、この関数が見かけ上2回呼び出され、2回目でエラーが発生したためです。

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

ブートストラップの例.

ルーティング(ngview)に組み込まれているAngularを使用している場合は、このディレクティブを使用できます。

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>
2
Der Hochstapler

代わりの方法は i-sref-active を使うことです

関連するui-srefディレクティブの状態がアクティブなときに要素にクラスを追加し、非アクティブなときにそれらを削除するためにui-srefと一緒に働くディレクティブ。主なユースケースは、「アクティブ」状態のメニューボタンを非表示のメニュー項目と区別して表示することで、ui-srefに依存するナビゲーションメニューの特殊な外観を単純化することです。

使用法:

ui-sref-active = 'class1 class2 class3' - 関連するui-srefの状態がアクティブのときはクラス "class1"、 "class2"、および "class3"がそれぞれdirective要素に追加され、非アクティブのときは削除されます。

例:
次のテンプレートがあるとします。

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

クラス名は、ディレクティブのリンク時に1回補間されます(それ以降の補間値の変更は無視されます)。複数のクラスをスペース区切り形式で指定できます。

$ state.go()にオプションを渡すには、ui-sref-optsディレクティブを使用してください。例:

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

コントローラにカスタム属性を持たせることについての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
jasontwong
'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>
1

私がこの方法を見つけた場所を覚えていることはできませんが、それはかなり単純でうまく機能します。

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
cfranklin

一部のページではテンプレートをレンダリングするだけでコントローラがまったくないため、コントローラを変更する必要のないソリューションが必要でした。 $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
szimek

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

あなたの設定でactiveタブを述べたように、今あなたはあなたの<li>タグまたは<a>タグにactiveクラスを追加する必要があります。好きです、

ng-class="{active: $route.current.activetab == 'about'}"

つまり、ユーザーがページについてをクリックするたびに、自動的に現在のタブが識別され、アクティブなCSSクラスが適用されます。

これが役に立つことを願っています!

0
imbond