309 votes

Comment définir la classe active de la barre navale bootstrap avec Angular JS ?

Si j'ai une barre de navigation dans bootstrap avec les éléments suivants

Home | About | Contact

Comment puis-je définir la classe active pour chaque élément de menu lorsqu'ils sont actifs ? En d'autres termes, comment puis-je définir class="active" lorsque la route angulaire est à

  1. #/ pour la maison
  2. #/about pour la page d'accueil
  3. #/contact pour la page de contact

0 votes

10 votes

C'est similaire, mais ce n'est pas une "manière angulaire" de mettre en évidence les boutons de navigation.

0 votes

599voto

myl Points 2252

Une méthode très élégante consiste à utiliser ng-controller pour exécuter un seul contrôleur en dehors du ng-view :

<div class="collapse navbar-collapse" ng-controller="HeaderController">
    <ul class="nav navbar-nav">
        <li ng-class="{ active: isActive('/')}"><a href="http://stackoverflow.com/">Home</a></li>
        <li ng-class="{ active: isActive('/dogs')}"><a href="http://stackoverflow.com/dogs">Dogs</a></li>
        <li ng-class="{ active: isActive('/cats')}"><a href="http://stackoverflow.com/cats">Cats</a></li>
    </ul>
</div>
<div ng-view></div>

et l'inclure dans controllers.js :

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

0 votes

Cela pourrait-il être mis dans une directive d'en-tête ?

1 votes

Oui, bien sûr que oui ! C'est d'ailleurs comme ça que je l'utilise moi-même :)

10 votes

Étant donné que je suis novice en la matière, j'apprécierais que vous me donniez un exemple de la manière d'intégrer cette directive dans un en-tête, s'il vous plaît.

51voto

dave Points 12645

J'ai juste écrit une directive pour gérer cela, donc vous pouvez simplement ajouter l'attribut bs-active-link au parent <ul> et chaque fois que l'itinéraire change, il trouvera le lien correspondant et ajoutera l'élément active à la classe correspondante <li> .

Vous pouvez le voir en action ici : http://jsfiddle.net/8mcedv3b/

Exemple HTML :

<ul class="nav navbar-nav" bs-active-link>
  <li><a href="http://stackoverflow.com/home">Home</a></li>
  <li><a href="http://stackoverflow.com/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');   
                };
            });     
        });
    }
}
}]);

1 votes

Je préfère cette solution à la réponse acceptée parce qu'il n'y a pas de duplication des données de configuration, c'est-à-dire que les routes elles-mêmes restent à un seul endroit et cela fonctionne tout simplement

1 votes

Cela a fonctionné pour moi en utilisant scope.$watch("$routeChangeSuccess", function () { .... au lieu de scope.$on("$routeChangeSuccess", function () {....

0 votes

J'ai dû le modifier un peu pour mes propres besoins, mais la réponse est géniale ! Assez facile à comprendre.

38voto

Olivier Points 1494

Vous pouvez jeter un coup d'œil à AngularStrap la directive navbar semble être ce que vous recherchez :

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

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

Pour utiliser cette directive :

  1. Téléchargez AngularStrap à partir de http://mgcrea.github.io/angular-strap/

  2. Incluez le script sur votre page après bootstrap.js :
    <script src="lib/angular-strap.js"></script>

  3. Ajoutez les directives à votre module :
    angular.module('myApp', ['$strap.directives'])

  4. Ajoutez la directive à votre barre de navigation :
    <div class="navbar" bs-navbar>

  5. Ajouter des regex sur chaque élément du nav :
    <li data-match-route="/about"><a href="#/about">About</a></li>

1 votes

Merci. Cela m'a aidé à corriger ma propre directive (l'élément manquant pour moi était l'élément scope.$watch )

0 votes

Pourquoi le element passée au sélecteur jquery ? $('...', element)

0 votes

@Lucio la méthode prend 2 arguments - jQuery(selector, context) -- le deuxième argument est pour passer le contexte du sélecteur, soit l'élément DOM parent ou un autre objet jQuery lui-même - lire la suite ici

33voto

Ender2050 Points 1331

Voici une approche simple qui fonctionne bien avec Angular.

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

Dans votre contrôleur AngularJS :

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

2 votes

Belle approche. Je l'ai un peu modifiée - j'ai utilisé la directive ng-class(ng-class={active : isActive('/View1')}) et j'ai mis à jour la fonction isActive pour qu'elle renvoie true/false au lieu du nom de la classe elle-même.

0 votes

J'ai tout copié comme votre exemple, et pour moi $location.path() n'a pas fonctionné... je suis passé à $location.url(), et ça a marché !

8voto

Mark At Ramp51 Points 2101

Tout d'abord, ce problème peut être résolu de plusieurs façons. Cette méthode n'est peut-être pas la plus élégante, mais elle est certainement efficace.

Voici une solution simple que vous devriez pouvoir ajouter à tout projet. Il vous suffit d'ajouter une "pageKey" ou une autre propriété lors de la configuration de votre itinéraire, que vous pouvez utiliser comme clé. De plus, vous pouvez implémenter un écouteur sur la méthode $routeChangeSuccess de l'objet $route pour écouter l'achèvement réussi d'un changement d'itinéraire.

Lorsque votre gestionnaire se déclenche, vous obtenez la clé de la page, et vous utilisez cette clé pour localiser les éléments qui doivent être "ACTIFS" pour cette page, et vous appliquez la classe ACTIVE.

Gardez à l'esprit que vous devez trouver un moyen de rendre TOUS les éléments "ACTIFS". Comme vous pouvez le voir, j'utilise la classe .pageKey sur mes éléments de navigation pour les désactiver tous, et j'utilise la classe .pageKey_{PAGEKEY} pour les activer individuellement. Les rendre tous inactifs serait considéré comme une approche naïve, vous obtiendriez potentiellement de meilleures performances en utilisant la route précédente pour ne rendre inactifs que les éléments actifs, ou vous pourriez modifier le sélecteur jquery pour ne sélectionner que les éléments actifs à rendre inactifs. L'utilisation de jquery pour sélectionner tous les éléments actifs est probablement la meilleure solution, car elle garantit que tout est nettoyé pour la route actuelle en cas de bogues css qui auraient pu être présents sur la route précédente.

Ce qui voudrait dire changer cette ligne de code :

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

à celui-ci

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

Voici un exemple de code :

Avec une barre de navigation bootstrap de

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

Et un module angulaire et un contrôleur comme le suivant :

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

0 votes

Vous devez faire défiler l'écran vers la droite pour voir les valeurs de pageKey sur les routes, c'est la clé de toute cette solution.

16 votes

Cela peut fonctionner, mais va à l'encontre des conseils généraux pour les applications Angular.js : en Angular, vous devez vous abstenir de toucher au DOM avec jQuery ; si vous devez toucher au DOM, écrivez une directive.

0 votes

C'est parfait, si la manipulation du dom vous dérange, il suffit d'ajouter une directive sur le .navbar, d'ajouter un événement $broadcast dans routechangesuccess, $rootScope.$broadcast('routeChanged', current.pageKey) ; et ensuite de le capturer sur votre directive et de faire les manipulations de dom là.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X