Как установить активный класс bootstrap navbar с Angular JS?

если у меня есть navbar в bootstrap с элементами

Home | About | Contact

Как установить активный класс для каждого пункта меню, когда они активные? То есть, как я могу установить class="active" когда угловой маршрут находится в

  1. #/ для дома
  2. #/about на странице
  3. #/contact на странице

25 ответов


очень элегантный способ-использовать 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>

и включить в контроллеры.js:

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

Я только что написал директиву для обработки этого, поэтому вы можете просто добавить атрибут bs-active-link родитель <ul> элемент, и в любое время маршрут изменился, он найдет соответствующую ссылку и добавит active класс к соответствующему <li>.

вы можете увидеть его в действии здесь: http://jsfiddle.net/8mcedv3b/

пример HTML:

<ul class="nav navbar-nav" bs-active-link>
  <li><a href="/home">Home</a></li>
  <li><a href="/contact">Contact</a></li>
</ul>

Javascript:

angular.module('appName')
.directive('bsActiveLink', ['$location', function ($location) {
return {
    restrict: 'A', //use as attribute 
    replace: false,
    link: function (scope, elem) {
        //after the route has changed
        scope.$on("$routeChangeSuccess", function () {
            var hrefs = ['/#' + $location.path(),
                         '#' + $location.path(), //html5: false
                         $location.path()]; //html5: true
            angular.forEach(elem.find('a'), function (a) {
                a = angular.element(a);
                if (-1 !== hrefs.indexOf(a.attr('href'))) {
                    a.parent().addClass('active');
                } else {
                    a.parent().removeClass('active');   
                };
            });     
        });
    }
}
}]);

вы можете посмотреть на AngularStrap, директива navbar кажется тем, что вы ищете:

https://github.com/mgcrea/angular-strap/blob/master/src/navbar/navbar.js

.directive('bsNavbar', function($location) {
  'use strict';

  return {
    restrict: 'A',
    link: function postLink(scope, element, attrs, controller) {
      // Watch for the $location
      scope.$watch(function() {
        return $location.path();
      }, function(newValue, oldValue) {

        $('li[data-match-route]', element).each(function(k, li) {
          var $li = angular.element(li),
            // data('match-rout') does not work with dynamic attributes
            pattern = $li.attr('data-match-route'),
            regexp = new RegExp('^' + pattern + '$', ['i']);

          if(regexp.test(newValue)) {
            $li.addClass('active');
          } else {
            $li.removeClass('active');
          }

        });
      });
    }
  };
});

использовать эту директиву:

  1. скачать AngularStrap из http://mgcrea.github.io/angular-strap/

  2. включить скрипт на Вашей странице после загрузчик.js:
    <script src="lib/angular-strap.js"></script>

  3. добавление директивы в модуле:
    angular.module('myApp', ['$strap.directives'])

  4. добавьте директиву в navbar:
    <div class="navbar" bs-navbar>

  5. добавить regexes на каждый элемент навигации:
    <li data-match-route="/about"><a href="#/about">About</a></li>


вот простой подход, который хорошо работает с углового.

<ul class="nav navbar-nav">
    <li ng-class="{ active: isActive('/View1') }"><a href="#/View1">View 1</a></li>
    <li ng-class="{ active: isActive('/View2') }"><a href="#/View2">View 2</a></li>
    <li ng-class="{ active: isActive('/View3') }"><a href="#/View3">View 3</a></li>
</ul>

в вашем контроллере AngularJS:

$scope.isActive = function (viewLocation) {
     var active = (viewLocation === $location.path());
     return active;
};

прежде всего, эта проблема может быть решена многими способами. Этот способ может быть не самый элегантный, но это cerntainly работает.

вот простое решение, которое вы должны иметь возможность добавить в любой проект. Вы можете просто добавить "pageKey" или какое-либо другое свойство при настройке маршрута, который вы можете использовать для отключения. Кроме того, можно реализовать прослушиватель в методе $routeChangeSuccess объекта $route для прослушивания успешного завершения маршрута изменение.

когда ваш обработчик срабатывает, вы получаете ключ страницы и используете этот ключ для поиска элементов, которые должны быть "активными" для этой страницы, и применяете активный класс.

имейте в виду, что вам нужен способ сделать все элементы "активными". Как вы можете видеть, я использую .класс pageKey на моих навигационных элементах, чтобы отключить их все ,и я использую.pageKey_{PAGEKEY}, чтобы индивидуально включить их. Переключение их всех в неактивное, будет считаться наивным подходом, потенциально вы получите лучшую производительность, используя предыдущий маршрут, чтобы сделать только активные элементы неактивными, или вы можете изменить селектор jquery, чтобы выбрать только активные элементы, которые будут сделаны неактивными. Использование jquery для выбора всех активных элементов, вероятно, лучшее решение, потому что оно гарантирует, что все очищено для текущего маршрута в случае каких-либо ошибок css, которые могли присутствовать на предыдущем маршруте.

что означало бы изменение этой строки кода:

$(".pagekey").toggleClass("active", false);

для этого один

$(".active").toggleClass("active", false);

вот пример кода:

учитывая загрузочный navbar

<div class="navbar navbar-inverse">
    <div class="navbar-inner">
        <a class="brand" href="#">Title</a>
        <ul class="nav">
            <li><a href="#!/" class="pagekey pagekey_HOME">Home</a></li>
            <li><a href="#!/page1/create" class="pagekey pagekey_CREATE">Page 1 Create</a></li>
            <li><a href="#!/page1/edit/1" class="pagekey pagekey_EDIT">Page 1 Edit</a></li>
            <li><a href="#!/page1/published/1" class="pagekey pagekey_PUBLISH">Page 1 Published</a></li>
        </ul>
    </div>
</div>

и угловой модуль и контроллер, как показано ниже:

<script type="text/javascript">

    function Ctrl($scope, $http, $routeParams, $location, $route) {

    }



    angular.module('BookingFormBuilder', []).
        config(function ($routeProvider, $locationProvider) {
            $routeProvider.
                when('/', { 
                   template: 'I\'m on the home page', 
                   controller: Ctrl, 
                   pageKey: 'HOME' }).
                when('/page1/create', { 
                   template: 'I\'m on page 1 create', 
                   controller: Ctrl, 
                   pageKey: 'CREATE' }).
                when('/page1/edit/:id', { 
                   template: 'I\'m on page 1 edit {id}', 
                   controller: Ctrl, pageKey: 'EDIT' }).
                when('/page1/published/:id', { 
                   template: 'I\'m on page 1 publish {id}', 
                   controller: Ctrl, pageKey: 'PUBLISH' }).
                otherwise({ redirectTo: '/' });

            $locationProvider.hashPrefix("!");
        }).run(function ($rootScope, $http, $route) {

            $rootScope.$on("$routeChangeSuccess", 
                           function (angularEvent, 
                                     currentRoute,
                                     previousRoute) {

                var pageKey = currentRoute.pageKey;
                $(".pagekey").toggleClass("active", false);
                $(".pagekey_" + pageKey).toggleClass("active", true);
            });

        });

</script>

Если вы работаете с угловым маршрутизатором,RouterLinkActive директива может использоваться очень элегантно:

<ul class="navbar-nav">
  <li class="nav-item"><a class="nav-link" routerLink="home" routerLinkActive="active">Home</a></li>
  <li class="nav-item"><a class="nav-link" routerLink="gallery" routerLinkActive="active">Gallery</a></li>
  <li class="nav-item"><a class="nav-link" routerLink="pricing" routerLinkActive="active">Prices</a></li>
  <li class="nav-item"><a class="nav-link" routerLink="contact" routerLinkActive="active">Contact</a></li>
</ul>

вы можете использовать угловой-ui-utils' :

<a ui-route ng-href="/">Home</a>
<a ui-route ng-href="/about">About</a>
<a ui-route ng-href="/contact">Contact</a>

или:

Контроллер Заголовка

/**
 * Header controller
 */
angular.module('myApp')
  .controller('HeaderCtrl', function ($scope) {
    $scope.menuItems = [
      {
        name: 'Home',
        url:  '/',
        title: 'Go to homepage.'
      },
      {
        name:   'About',
        url:    '/about',
        title:  'Learn about the project.'
      },
      {
        name:   'Contact',
        url:    '/contact',
        title:  'Contact us.'
      }
    ];
  });

страница индекса

<!-- index.html: -->
<div class="header" ng-controller="HeaderCtrl">
  <ul class="nav navbar-nav navbar-right">
    <li ui-route="{{menuItem.url}}" ng-class="{active: $uiRoute}"
      ng-repeat="menuItem in menuItems">
      <a ng-href="#{{menuItem.url}}" title="{{menuItem.title}}">
        {{menuItem.name}}
      </a>
    </li>
  </ul>
</div>

если вы используете ui-utils, вы также можете быть заинтересованы в ui-router для управления частичными / вложенными представлениями.


Я нахожу все эти ответы слишком сложные для меня, к сожалению. Поэтому я создал небольшую директиву, которая должна работать на основе navbar:

app.directive('activeLink', function () {
    return {
        link: function (scope, element, attrs) {
            element.find('.nav a').on('click', function () {
                angular.element(this)
                    .parent().siblings('.active')
                    .removeClass('active');
                angular.element(this)
                    .parent()
                    .addClass('active');
            });
        }
    };
});

использование:

<ul class="nav navbar-nav navbar-right" active-link>
    <li class="nav active"><a href="home">Home</a></li>
    <li class="nav"><a href="foo">Foo</a></li>
    <li class="nav"><a href="bar">Bar</a></li>
</ul>

Я использую директиву ng-class с $location для ее достижения.

<ul class="nav">
<li data-ng-class="{active: ($location.path() == '/') }">
    <a href="#/">Carpeta Amarilla</a>
</li>
<li class="dropdown" data-ng-class="{active: ($location.path() == '/auditoria' || $location.path() == '/auditoria/todos') }">
    <a class="dropdown-toggle" data-toggle="dropdown" href="#">
        Auditoria
        <b class="caret"></b>
    </a>
    <ul class="dropdown-menu pull-right">
        <li data-ng-class="{active: ($location.path() == '/auditoria') }">
            <a href="#/auditoria">Por Legajo</a>
        </li>
        <li data-ng-class="{active: ($location.path() == '/auditoria/todos') }">
            <a href="#/auditoria/todos">General</a>
        </li>
    </ul>
</li>
</ul>

он требует, чтобы navbar был внутри главного контроллера с доступом к $location service, как это:

bajasApp.controller('MenuCntl', ['$scope','$route', '$routeParams', '$location', 
   function MenuCntl($scope, $route, $routeParams, $location) {
   $scope.$route = $route;
   $scope.$location = $location;
   $scope.$routeParams = $routeParams;
}]);

вы можете достичь этого с условным выражением в угловом выражении, например:

<a href="#" class="{{ condition ? 'active' : '' }}">link</a>

это, как говорится, я нахожу угловую директиву более "правильным" способом сделать это, хотя аутсорсинг много этой мини-логики может несколько загрязнить вашу кодовую базу.

Я использую conditionals для стиля GUI время от времени во время разработки, потому что это немного быстрее, чем создание директив. Я не мог бы сказать вам пример, в котором они на самом деле долго оставался в базе кода. В конце концов я либо превращаю его в директиву, либо нахожу лучший способ решить проблему.


Если вы используете ui-router, следующий пример должен удовлетворить ваши потребности на основе комментария @DanPantry к принятому ответу без добавления кода на стороне контроллера:

<div class="collapse navbar-collapse" ng-controller="HeaderController">
    <ul class="nav navbar-nav">
        <li ui-sref-active="active"><a ui-sref="app.home()" href="/">Home</a></li>
        <li ui-sref-active="active"><a ui-sref="app.dogs()" href="/dogs">Dogs</a></li>
        <li ui-sref-active="active"><a ui-sref="app.cats()" href="/cats">Cats</a></li>
    </ul>
</div>
<div ng-view></div>

вы можете проверить docs для получения дополнительной информации о нем.


Если вы не хотите использовать AngularStrap затем эта директива должна сделать трюк!. Это модификация https://stackoverflow.com/a/16231859/910764.

JavaScript

angular.module('myApp').directive('bsNavbar', ['$location', function ($location) {
  return {
    restrict: 'A',
    link: function postLink(scope, element) {
      scope.$watch(function () {
        return $location.path();
      }, function (path) {
        angular.forEach(element.children(), (function (li) {
          var $li = angular.element(li),
            regex = new RegExp('^' + $li.attr('data-match-route') + '$', 'i'),
            isActive = regex.test(path);
          $li.toggleClass('active', isActive);
        }));
      });
    }
  };
}]);

HTML-код

<ul class="nav navbar-nav" bs-navbar>
  <li data-match-route="/home"><a href="#/home">Home</a></li>
  <li data-match-route="/about"><a href="#/about">About</a></li>
</ul>

Примечание: вышеупомянутые классы HTML предполагают, что вы используете Bootstrap 3.x


вот мой взгляд на это. Немного комбинации ответов, найденных на этом посте. У меня был немного другой случай, поэтому мое решение включает разделение меню на собственный шаблон, который будет использоваться в определении директивы Ojbect, а затем добавьте мою navbar на страницу, на которой она мне нужна. В принципе, у меня была страница входа, которую я не хотел включать в меню, поэтому я использовал ngInclude и вставил эту директиву при входе в систему:


расширяя myl ответ, мне нужно было это для обработки такой структуры.

- index

- события ---события-список
--- event-edit
---событие-картой

мест
---место-список
--- место-редактировать
--- место-карта

поэтому вместо сопоставления мне пришлось использовать indexOf, чтобы проверить дочерние ссылки, которые отформатированы в соответствии с условием. Итак, для "событий":

<li ng-class="{ active: isActive('/event')}" class="divider-vertical dropdown">


function NavController($scope, $location) { 
$scope.isActive = function (viewLocation) {
    var s=false;
    if($location.path().indexOf(viewLocation) != -1){
     s = true;
    }
    return s;
};}

Это простое решение

<ul class="nav navbar-nav navbar-right navbar-default menu">
  <li ng-class="menuIndice == 1 ? 'active':''">
    <a ng-click="menuIndice = 1" href="#/item1">item1</a>
  </li>
  <li ng-class="menuIndice == 2 ? 'active':''">
    <a ng-click="menuIndice = 2" href="#/item2">item2</a>
  </li>
  <li ng-class="menuIndice == 3 ? 'active':''">
    <a ng-click="menuIndice = 3" href="#/item3">item3</a>
  </li>
</ul>

в сочетании с ответом @ Olivier AngularStrap я также реализовал ответ кевинкнельсона из:https://github.com/twbs/bootstrap/issues/9013.

изначально панель навигации Bootstrap3 не была предназначена для одностраничного (например, углового) приложения, и поэтому меню, когда на маленьком экране не сворачивалось при щелчке.


JavaScript

/**
 * Main AngularJS Web Application
 */

var app = angular.module('yourWebApp', [
    'ngRoute'
]);


/**
 * Setup Main Menu
 */

app.controller('MainNavCtrl', [ '$scope', '$location', function ( $scope, $location) {
    $scope.menuItems = [
        {
            name: 'Home',
            url:  '/home',
            title: 'Welcome to our Website'
        },
        {
            name: 'ABOUT',
            url:  '/about',
            title: 'Know about our work culture'
        },
        {
            name:   'CONTACT',
            url:    '/contact',
            title:  'Get in touch with us'
        }
    ];

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

HTML-код

  <div class="navbar-collapse collapse" ng-controller="MainNavCtrl">
    <ul id="add-magic-line" class="nav navbar-nav navbar-right">
      <li data-ng-class="{current_page_item: isActive('{{ menuItem.url }}')}" data-ng-repeat="menuItem in menuItems">
        <a data-ng-href="#{{menuItem.url}}" title="{{menuItem.title}}">
          {{menuItem.name}}
        </a>
      </li>
    </ul>
  </div>

спасибо @Pylinux. Я использовал его технику, а также изменил ее для поддержки "одного" уровня раскрывающегося меню (sub ul/li), так как это то, что мне нужно. См. его в действии в ссылке fiddle ниже.

обновлена Скрипка на основе ответа pylinux -http://jsfiddle.net/abhatia/en4qxw6g/

Я сделал следующие три изменения, чтобы поддержать одно выпадающее меню уровня:
1. Добавлено значение класса DD (раскрывающийся список) для элемента " a под li, который должен иметь список sub ul.

         <li><a class="dd">This link points to #/fun5</a>
          <ul>
            <li><a href="#/fun6?some=data">This link points to #/fun6</a>
            </li>
            <li><a href="#/fun7?some=data">This link points to #/fun7</a>
            </li>
            <li><a href="#/fun8?some=data">This link points to #/fun8</a>
            </li>
            <li><a href="#/fun9?some=data">This link points to #/fun9</a>
            </li>
          </ul>
        </li>


2. Обновлен Javascript, чтобы добавить следующую новую логику.

 if(angular.element(li).parent().parent().children('a').hasClass("dd"))
 {angular.element(li).parent().parent().children('a.dd').addClass('active');}


3. Обновлен CSS, чтобы добавить следующее:

a.active {background-color:red;}

надеюсь, это будет полезно для тех, кто хочет реализовать выпадающее меню одного уровня.


вы также можете использовать эту директиву active-link https://stackoverflow.com/a/23138152/1387163

Parent li получит активный класс, когда местоположение соответствует /url:

<li>
    <a href="#!/url" active-link active-link-parent>
</li>

Я предлагаю использовать директиву по ссылке. здесь - это скрипка.

но это еще не идеально. Следите за hashbangs ;)

вот javascript для директивы:

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 not return including hashbang
        scope.location = location;
        scope.$watch('location.path()', function(newPath) {
          if (path === newPath) {
            element.addClass(clazz);
          } else {
            element.removeClass(clazz);
          }
        });
      }
    };
  }]);

а вот как он будет использоваться в 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>

после укладки с помощью css:

.active{ color:red; }

просто чтобы добавить мои два цента в дебатах, я сделал чистый угловой модуль (без jquery), и он также будет работать с хэш-URL-адресами, содержащими данные. (Я.гр. #/this/is/path?this=is&some=data)

вы просто добавляете модуль как зависимость и auto-active одному из предков меню. Вот так:

<ul auto-active>
    <li><a href="#/">main</a></li>
    <li><a href="#/first">first</a></li>
    <li><a href="#/second">second</a></li>
    <li><a href="#/third">third</a></li>
</ul>

и модуль выглядит так:

(function () {
    angular.module('autoActive', [])
        .directive('autoActive', ['$location', function ($location) {
        return {
            restrict: 'A',
            scope: false,
            link: function (scope, element) {
                function setActive() {
                    var path = $location.path();
                    if (path) {
                        angular.forEach(element.find('li'), function (li) {
                            var anchor = li.querySelector('a');
                            if (anchor.href.match('#' + path + '(?=\?|$)')) {
                                angular.element(li).addClass('active');
                            } else {
                                angular.element(li).removeClass('active');
                            }
                        });
                    }
                }

                setActive();

                scope.$on('$locationChangeSuccess', setActive);
            }
        }
    }]);
}());

* (вы можете, конечно, просто использовать директивную часть)

* * также стоит отметить, что это не работает для пустых хэшей (Я.гр. example.com/# или просто example.com) он должен иметь по крайней мере example.com/#/ или просто example.com#/. Но это происходит автоматически с ngResource и тому подобное.


это сделал трюк для меня:

  var domain = '{{ DOMAIN }}'; // www.example.com or dev.example.com
  var domain_index =  window.location.href.indexOf(domain);
  var long_app_name = window.location.href.slice(domain_index+domain.length+1); 
  // this turns http://www.example.com/whatever/whatever to whatever/whatever
  app_name = long_app_name.slice(0, long_app_name.indexOf('/')); 
  //now you are left off with just the first whatever which is usually your app name

затем вы используете jquery (также работает с angular), чтобы добавить класс active

$('nav a[href*="' + app_name+'"]').closest('li').addClass('active');

и, конечно, css:

.active{background:red;}

это работает, если у вас есть html, как это:

<ul><li><a href="/ee">ee</a></li><li><a href="/dd">dd</a></li></ul>

это будет atumatically добавить класс active с помощью url страницы и цвет фона красный, если ваш в www.somesite.com/ee thaen ee - это "приложение", и оно будет активным


на это долго отвечали, но я думал, что поделюсь своим путем:

.run(function($rootScope, $state){
 $rootScope.$state = $state;
});

шаблон:

<ul class="nav navbar-nav">
    <li ng-class="{ active: $state.contains('View1') }"><a href="...">View 1</a></li>
    <li ng-class="{ active: $state.contains('View2') }"><a href="...">View 2</a></li>
    <li ng-class="{ active: $state.contains('View3') }"><a href="...">View 3</a></li>
</ul>

для тех, кто использует ui-router:

<ul class="nav navbar-nav">
        <li ui-sref-active="active"><a href="...">View 1</a></li>
        <li ui-sref-active="active"><a href="...">View 2</a></li>
        <li ui-sref-active="active"><a href="...">View 3</a></li>
</ul>

для точного соответствия (например, вложенные состояния?) использовать $state.name === 'full/path/to/state' или ui-sref-active-eq="active"


вот еще одно решение для тех, кто может быть заинтересован. Преимущество этого в том, что у него меньше зависимостей. Черт возьми, он работает и без веб-сервера. Так что это полностью клиентская сторона.

HTML:

<nav class="navbar navbar-inverse" ng-controller="topNavBarCtrl"">
<div class="container-fluid">
    <div class="navbar-header">
        <a class="navbar-brand" href="#"><span class="glyphicon glyphicon-home" aria-hidden="true"></span></a>
    </div>
    <ul class="nav navbar-nav">
        <li ng-click="selectTab()" ng-class="getTabClass()"><a href="#">Home</a></li>
        <li ng-repeat="tab in tabs" ng-click="selectTab(tab)" ng-class="getTabClass(tab)"><a href="#">{{ tab }}</a></li>
    </ul>
</div>

объяснение:

здесь мы генерируем ссылки динамически из модели angularjs, используя директиву ng-repeat. Магия происходит с методами selectTab() и getTabClass() определенный в регуляторе для этого navbar представленный ниже.

:
angular.module("app.NavigationControllersModule", [])

// Constant named 'activeTab' holding the value 'active'. We will use this to set the class name of the <li> element that is selected.
.constant("activeTab", "active")

.controller("topNavBarCtrl", function($scope, activeTab){
    // Model used for the ng-repeat directive in the template.
    $scope.tabs = ["Page 1", "Page 2", "Page 3"];

    var selectedTab = null;

    // Sets the selectedTab.
    $scope.selectTab = function(newTab){
       selectedTab = newTab;
    };

    // Sets class of the selectedTab to 'active'.
    $scope.getTabClass = function(tab){
       return selectedTab == tab ? activeTab : "";
    };
});

объяснение:

selectTab() метод называется using


просто вам нужно добавить требуемый active-класс с требуемым цветовым кодом.

Ex:ng-class="{'active': currentNavSelected}" ng-click="setNav"