¿Cómo configurar la clase activa bootstrap navbar con Angular JS?


306

Si tengo una barra de navegación en bootstrap con los elementos

Home | About | Contact

¿Cómo configuro la clase activa para cada elemento del menú cuando están activos? Es decir, ¿cómo puedo configurar class="active"cuando la ruta angular está en

  1. #/ para casa
  2. #/about para la página acerca de
  3. #/contact para la página de contacto


10
Esto es similar, pero no es una "forma angular" de resaltar los botones de navegación.
usuario1876508


Si está utilizando enrutamiento angular , tenga en cuenta que la respuesta perfecta se encuentra debajo: stackoverflow.com/a/43822400/474189 .
Duncan Jones

Respuestas:


598

Una forma muy elegante es usar ng-controller para ejecutar un solo controlador fuera de 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>

e incluir en controllers.js:

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

10
Como soy nuevo en esto, agradecería si pudiera proporcionar un ejemplo sobre cómo poner esto en una directiva de encabezado, por favor.
mono68

41
Sugeriría usar en su return $location.path().indexOf(viewLocation) == 0;lugar para que también pueda atrapar páginas con parámetros
Sven Hecht

77
Esto hace el trabajo, pero su casi elegant- el nombre de la ruta por ejemplo, /dogstiene que ser duplicado en la llamada aisActive('/dogs')
Wal

77
Si está utilizando UI Router, puede usar las ui-sref-active/ui-sref-active-eqdirectivas, es decir. ui-sref-active-eq='active'o ui-sref-active='active'para lograr los mismos resultados
Dan Pantry

10
@SvenHecht La preocupación con esa lógica es que un enlace de página de inicio (/) coincidiría con cualquier otra ruta.
mwotton

51

Acabo de escribir una directiva para manejar esto, por lo que simplemente puede agregar el atributo bs-active-linkal <ul>elemento padre , y cada vez que cambie la ruta, encontrará el enlace correspondiente y agregará la activeclase a la correspondiente <li>.

Puedes verlo en acción aquí: http://jsfiddle.net/8mcedv3b/

HTML de ejemplo:

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

1
Me gusta más que la respuesta aceptada porque no hay duplicación de datos de configuración, es decir, las rutas permanecen en un solo lugar y esto simplemente funciona ...
Aaron Anodide

1
funcionó para mí usando el alcance. $ watch ("$ routeChangeSuccess", function () {.... en lugar del alcance. $ on ("$ routeChangeSuccess", function () {....
aemad

Tuve que ajustar esto un poco para mis propios fines, ¡pero una respuesta increíble! Lo suficientemente fácil de entender.
Tony Anziano

38

Puede echar un vistazo a AngularStrap , la directiva de barra de navegación parece ser lo que está buscando:

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

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

Para usar esta directiva:

  1. Descargue AngularStrap desde http://mgcrea.github.io/angular-strap/

  2. Incluya el script en su página después de bootstrap.js:
    <script src="lib/angular-strap.js"></script>

  3. Agregue las directivas a su módulo:
    angular.module('myApp', ['$strap.directives'])

  4. Agregue la directiva a su barra de navegación:
    <div class="navbar" bs-navbar>

  5. Agregue expresiones regulares en cada elemento de navegación:
    <li data-match-route="/about"><a href="#/about">About</a></li>


1
Gracias. Esto me ayudó a arreglar mi propia directiva (la idea que faltaba para mí era la scope.$watch)
Ngure Nyaga

¿Por qué se pasa la elementvariable al selector jquery? $('...', element)
Lucio

@Lucio, el método toma 2 argumentos: jQuery (selector, contexto): el segundo argumento es pasar el contexto del selector, ya sea el elemento DOM principal u otro objeto jQuery en sí mismo. Lea más aquí
CoderTR

Esto funciona perfectamente para mí; sin embargo, tuve que hacer un cambio, por mgcrea.github.io/angular-strap, la directiva para agregar a su módulo mgcrea.ngStrapno es$strap.directives
Vixxd

33

Aquí hay un enfoque simple que funciona bien con 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>

Dentro de su controlador AngularJS:

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

2
Buen enfoque. Lo modifiqué un poco: utilicé la directiva ng-class (ng-class = {active: isActive ('/ View1')}) y actualicé la función isActive para devolver verdadero / falso en lugar del nombre de la clase.
patelsan

He copiado todo como su ejemplo, y para mí $ location.path () no funcionó ... cambié a $ location.url (), ¡y funcionó!
Paulo Oliveira

14

Si está trabajando con un enrutador angular, la directiva RouterLinkActive se puede usar con mucha elegancia:

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

2
Creo que esta debería ser la respuesta aceptada (para angular 2+ al menos), ¿por qué implementar algo ya está implementado? Pero para Bootstrap 3.3 la activeclase debería estar en el <li>(puede poner el routerLinkActive con el routerLink o su padre)
PhoneixS

3
Nota: si lo usa /como su página de inicio, routerLinkActivelo considerará activo para cualquier URL que comience /, por ejemplo /foo/, /foo/baretc. Para que coincida exactamente, necesita routerLinkActive="active" [routerLinkActiveOptions]="{exact:true}".
Duncan Jones

8

En primer lugar, este problema se puede resolver de muchas maneras. Es posible que este camino no sea el más elegante, pero ciertamente funciona.

Aquí hay una solución simple que debería poder agregar a cualquier proyecto. Simplemente puede agregar una "pageKey" o alguna otra propiedad cuando configure su ruta que puede usar para desconectar. Además, puede implementar un escucha en el método $ routeChangeSuccess del objeto $ route para escuchar la finalización exitosa de un cambio de ruta.

Cuando se activa su controlador, obtiene la clave de página, y la usa para localizar elementos que deben ser "ACTIVOS" para esta página, y aplica la clase ACTIVE.

Tenga en cuenta que necesita una forma de hacer TODOS los elementos "EN ACTIVO". Como puede ver, estoy usando la clase .pageKey en mis elementos de navegación para desactivarlos, y estoy usando .pageKey_ {PAGEKEY} para activarlos individualmente. Cambiarlos a inactivos se consideraría un enfoque ingenuo, potencialmente obtendría un mejor rendimiento al usar la ruta anterior para hacer que solo los elementos activos estén inactivos, o podría alterar el selector jquery para seleccionar solo los elementos activos que se volverán inactivos. El uso de jquery para seleccionar todos los elementos activos es probablemente la mejor solución, ya que garantiza que todo se limpie para la ruta actual en caso de cualquier error css que pueda haber estado presente en la ruta anterior.

Lo que significaría cambiar esta línea de código:

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

a este

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

Aquí hay un código de muestra:

Dada una barra de navegación de arranque 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>

Y un módulo angular y controlador como el siguiente:

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

Debe desplazarse hacia la derecha para ver los valores de pageKey en las rutas, esa es la clave de toda esta solución.
Mark At Ramp51

16
Esto puede funcionar, pero va en contra del consejo general para las aplicaciones de Angular.js: en Angular debe abstenerse de meterse con el DOM usando jQuery; Si tiene que tocar el DOM, escriba una directiva.
Paulo Scardine

esto es perfecto, si la manipulación dom le molesta, simplemente agregue una directiva en el .navbar, agregue un evento $ broadcast en routechangesuccess, $ rootScope. $ broadcast ('routeChanged', current.pageKey); y luego capturarlo en su directiva y hacer las manipulaciones de dom allí
Irshu

7

En realidad, puede usar la directiva angular-ui-utilsui-route :

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

o:

Controlador de encabezado

/**
 * 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.'
      }
    ];
  });

Página de inicio

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

Si está utilizando ui-utils, también puede estar interesado en ui-router para administrar vistas parciales / anidadas.


El enfoque de ruta ui es muy seductor, pero hasta ahora no pude hacerlo funcionar en el proyecto generado por el generador angular de Yeoman.
gabuzo

@gabuzo: ¿Instalaste angular-ui-utils a través de Bower?
Lèse majesté

6

Todas estas respuestas me parecen un poco complicadas, lo siento. Así que he creado una pequeña directiva que debería funcionar por barra de navegación:

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

Uso:

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

Su método funciona solo después de hacer clic en un enlace en la barra de navegación. Si comienza en una URL que es "Foo", "Home" inicialmente se seleccionará aún. por ejemplo, comenzando en localhost: 9000 / # / contact hace que el Inicio aparezca seleccionado en la barra de navegación.
Adam Plocher

5

Utilizo la directiva ng-class con $ location para lograrlo.

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

Requiere que la barra de navegación esté dentro de un controlador principal con acceso al servicio $ location como este:

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

4

Puede lograr esto con un condicional en una expresión angular, como:

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

Dicho esto, creo que una directiva angular es la forma más "adecuada" de hacerlo, a pesar de que la subcontratación de gran parte de esta mini lógica puede contaminar de alguna manera su base de código.

Utilizo condicionales para el estilo de la GUI de vez en cuando durante el desarrollo, porque es un poco más rápido que crear directivas. Sin embargo, no podría decirte una instancia en la que realmente permanecieron en la base del código durante mucho tiempo. Al final, lo convierto en una directiva o encuentro una mejor manera de resolver el problema.


4

Si usa ui-router , el siguiente ejemplo debería satisfacer sus necesidades según el comentario de @ DanPantry sobre la respuesta aceptada sin agregar ningún código del lado del controlador:

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

Puede consultar los documentos para obtener más información al respecto.


¡Perfecto! seguramente la solución más limpia
Aryeh Beitz

Simplemente la mejor solución. quien no usa ui.router!
rocío

3

Si prefiere no usar AngularStrap, entonces esta directiva debería ser la solución . Esta es una modificación de 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>

Nota: Las clases HTML anteriores suponen que está utilizando Bootstrap 3.x


3

Aquí está mi opinión al respecto. Una pequeña combinación de respuestas encontradas en esta publicación. Tuve un caso ligeramente diferente, por lo que mi solución consiste en separar el menú en su propia plantilla para usar dentro de la Directiva Definición Ojbect y luego agregar mi barra de navegación a la página en la que lo necesitaba. Básicamente, tenía una página de inicio de sesión en la que no quería incluir mi menú, así que utilicé ngInclude e inserté esta directiva cuando inicié sesión:

DIRECTIVA:

module.directive('compModal', function(){


return {
    restrict: 'E',
    replace: true,
    transclude: true,
    scope: true,
    templateUrl: 'templates/menu.html',
    controller: function($scope, $element, $location){
        $scope.isActive = function(viewLocation){

            var active = false;

            if(viewLocation === $location.path()){
                active = true;
            }

            return active;

        }
    }
 }
});

PLANTILLA DIRECTIVA (templates / menu.html)

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

HTML QUE INCLUYE LA DIRECTIVA

<comp-navbar/>

Espero que esto ayude


1
La función se puede acortar a solo $ scope.isActive = function (viewLocation) {return viewLocation === $ location.path (); };
WP McNeill

2

Extendiendo mi respuesta, necesitaba esto para manejar una estructura como esta.

-índice

-eventos <-active
--- lista de
eventos
--- edición de eventos --- mapa de eventos <-clicked

-places
--- place-list
--- place-edit
--- place-map

así que en lugar de hacer coincidir, tuve que usar indexOf, para validar enlaces secundarios que están formateados para coincidir con la condición. Entonces para 'eventos':

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

2

Esta es una solución simple.

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

1

Junto con la respuesta AngularStrap de @ Olivier, también implementé la respuesta de kevinknelson de: https://github.com/twbs/bootstrap/issues/9013 .

De forma nativa, la barra de navegación Bootstrap3 no fue diseñada para una aplicación de una sola página (por ejemplo, Angular) y, por lo tanto, el menú cuando se encontraba en una pantalla pequeña no se colapsó al hacer clic.


1

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>

Esto no funciona, no agrega la clase actual al DOM
TheBlackBenzKid

1

Gracias a @Pylinux . He usado su técnica y también la modifiqué para admitir "un" nivel de menú desplegable (sub ul / li), ya que eso es lo que necesitaba. Véalo en acción en el siguiente enlace de violín.

Fiddle actualizado basado en la respuesta de pylinux: http://jsfiddle.net/abhatia/en4qxw6g/

Hice los siguientes tres cambios, para admitir un menú desplegable de un nivel:
1. Agregué un valor de clase de dd (menú desplegable) para el elemento "a" debajo de li que necesita tener una lista ulterior.

         <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 actualizado para agregar la siguiente lógica nueva.

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


3. CSS actualizado para agregar lo siguiente:

a.active {background-color:red;}

Esperemos que esto sea útil para alguien que busque implementar un menú desplegable de un solo nivel.


1

Use un objeto como una variable de cambio.
Puede hacer esto en línea simplemente con:

<ul class="nav navbar-nav">
   <li ng-class="{'active':switch.linkOne}" ng-click="switch = {linkOne: true}"><a href="/">Link One</a></li>
   <li ng-class="{'active':switch.linkTwo}" ng-click="switch = {link-two: true}"><a href="/link-two">Link Two</a></li>
</ul>

Cada vez que hace clic en un enlace, el objeto de cambio se reemplaza por un nuevo objeto donde solo la propiedad correcta del objeto de cambio es verdadera. Las propiedades indefinidas se evaluarán como falsas, por lo que los elementos que dependen de ellas no tendrán asignada la clase activa.



0

Sugiero usar una directiva en un enlace. Aquí está el violín.

Pero aún no es perfecto. Cuidado con los hashbangs;)

Aquí está la directiva JavaScript para:

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

y así es como se usaría en 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>

luego peinado con css:

.active{ color:red; }

0

Solo para agregar mis dos centavos en el debate, hice un módulo angular puro (sin jquery), y también funcionará con URL hash que contengan datos. ( ig #/this/is/path?this=is&some=data )

Simplemente agrega el módulo como una dependencia y auto-activea uno de los antepasados ​​del menú. Me gusta esto:

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

Y el módulo se ve así:

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

* (Por supuesto, solo puede usar la parte directiva)

** También vale la pena notar que esto no funciona para los hashes vacíos ( ig example.com/# o simplemente example.com) que necesita tener al menos example.com/#/o solo example.com#/. Pero esto sucede automáticamente con ngResource y similares.


Eso es simplemente desagradable. La razón para usar Angular es la simplicidad.
Tom Stickel

0

Esto hizo el truco para mí:

  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

luego usas jquery (también funciona con angular) para agregar la clase activa

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

y por supuesto el css:

.active{background:red;}

esto funciona si tienes tu html como este:

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

esto agregará automáticamente la clase activa usando la URL de la página y coloreará su fondo a rojo si está en www.somesite.com/ee que es la 'aplicación' y estará activa


0

Esto fue respondido por mucho tiempo, pero pensé en compartir mi camino:

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

Modelo:

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

Para aquellos que usan 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>

Por coincidencia exacta (por ejemplo, estados anidados?) El uso $state.name === 'full/path/to/state'oui-sref-active-eq="active"


0

Aquí hay otra solución para cualquiera que pueda estar interesado. La ventaja de esto es que tiene menos dependencias. Diablos, también funciona sin un servidor web. Entonces es completamente del lado del cliente.

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>

Explicación:

Aquí estamos generando los enlaces dinámicamente a partir de un modelo angularjs utilizando la directiva ng-repeat. La magia ocurre con los métodos selectTab()y getTabClass()definidos en el controlador para esta barra de navegación presentada a continuación.

Controlador:

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

Explicación:

selectTab()El método se llama usando la ng-clickdirectiva. Entonces, cuando se hace clic en el enlace, la variable selectedTabse establece con el nombre de este enlace. En el HTML, puede ver que se llama a este método sin ningún argumento para la pestaña Inicio, de modo que se resaltará cuando se cargue la página.

El getTabClass()método se llama vía ng-classdirectiva en el HTML. Este método verifica si la pestaña en la que se encuentra es la misma que el valor de la selectedTabvariable. Si es verdadero, devuelve "activo", de lo contrario, devuelve "", que se aplica como nombre de clase por ng-classdirectiva. Entonces, cualquier css que haya aplicado a la clase activese aplicará a la pestaña seleccionada.


¿Qué sucede si navego a otra pestaña por cualquier otro medio?
Miguel Carvajal

¿Qué quieres decir?
kovac

por ejemplo, un enlace en la página que lo lleva a otra página que está dentro de otra pestaña. ¿Tiene sentido?
Miguel Carvajal

En ese caso, creo que es mejor usar ui-router como lo sugirió Muli Yulzary en la respuesta anterior. ui-router asigna un estado a cada url. Entonces, una vez que alguien haga clic en el enlace, el usuario será enrutado a ese enlace y se actualizará el estado de la aplicación angular. Entonces ui-sref = "active" resaltará la pestaña. Aquí hay documentación para angular 2: ui-router.github.io/ng2 . Además, en lugar de usar el aspecto normal de bootstrap en UI Bootstrap hecho para usar con aplicaciones angulares. IU Bootstrap: angular-ui.github.io/bootstrap
Kovac

0

Solo tendrá que agregar la activeclase requerida con el código de color requerido.

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

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.