Come evidenziare una voce di menu corrente?


205

AngularJS aiuta in qualche modo a impostare una activeclasse sul collegamento per la pagina corrente?

Immagino che ci sia un modo magico per farlo, ma non riesco a trovarlo.

Il mio menu è simile a:

 <ul>
   <li><a class="active" href="/tasks">Tasks</a>
   <li><a href="/actions">Tasks</a>
 </ul>

e ho controller per ciascuno di essi nei miei percorsi: TasksControllere ActionsController.

Ma non riesco a capire un modo per associare la classe "attiva" sui acollegamenti ai controller.

Qualche suggerimento?

Risposte:


265

in mostra

<a ng-class="getClass('/tasks')" href="/tasks">Tasks</a>

sul controller

$scope.getClass = function (path) {
  return ($location.path().substr(0, path.length) === path) ? 'active' : '';
}

Con questo il collegamento task avrà la classe attiva in qualsiasi url che inizia con '/ compiti' (es. '/ Compiti / 1 / rapporti')


4
Questo finirebbe per abbinare sia "/" che "/ any" o se hai più voci di menu con URL simili, come "/ test", "/ test / this", "/ test / this / path" se eri su / test, evidenzierebbe tutte quelle opzioni.
Ben Lesh,

3
Ho cambiato questo in if ($ location.path () == path) e, y path è "/ blah" ecc.
Tim

113
Preferisco la notazione ngClass="{active: isActive('/tasks')}, dove isActive()restituire un valore booleano in quanto disaccoppia il controller e il markup / stile.
Ed Hinchliffe,

6
Nel caso in cui qualcuno si chiedesse del codice per non raddoppiare se il percorso è "/", eccolo (scusate la formattazione): $ scope.getClass = function (path) {if ($ location.path (). substr (0, path.length) == path) {if (path == "/" && $ location.path () == "/") {return "attivo"; } else if (path == "/") {return ""; } return "active"} else {return ""}}

1
EdHinchliffe ha già sottolineato che questo mescola markup e logica. Porta anche alla duplicazione del percorso e potrebbe quindi essere soggetto a errori di copia e incolla. Ho scoperto che l'approccio della direttiva di @kfis, sebbene più righe, è più riutilizzabile e mantiene il markup più pulito.
A. Murray,

86

Suggerisco di usare una direttiva su un collegamento.

Ma non è ancora perfetto. Fai attenzione agli hashbang;)

Ecco il javascript per direttiva:

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

ed ecco come sarebbe usato in 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>

successivamente styling con css:

.active { color: red; }

Non sono sicuro di cosa intendi per "attenzione agli hashbang". Sembra che funzionerebbe sempre. Potresti fornire un controesempio?
Andriy Drozdyuk,

7
Se stai provando a utilizzare Bootstrap e devi impostare in base all'hash dell'href di una a all'interno di una li, allora usa var path = $(element).children("a")[0].hash.substring(1);. Funzionerà con uno stile come<li active-link="active"><a href="#/dashboard">Dashboard</a></li>
Dave,

2
Vorrei cambiare scope.$watch('location.path()', function(newPath) {per scope.$on('$locationChangeStart', function(){.
sanfilippopablo,

2
Se stai usando ng-href basta cambiare: var path = attrs.href; in var path = attrs.href||attrs.ngHref;
William Neely

Se usi Bootstrap e devi mettere la classe attiva su <li>, puoi cambiare element.addClass(clazz);inelement.parent().addClass(clazz);
JamesRLamar il

47

Ecco un approccio semplice che funziona bene con Angular.

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

All'interno del controller AngularJS:

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

Questa discussione ha una serie di altre risposte simili.

Come impostare la classe attiva navst bootstrap con Angular JS?


1
Rimuovere la variabile poiché non è necessaria. Restituisci semplicemente il risultato del confronto. return viewLocation === $location.path()
afarazit,

33

Solo per aggiungere i miei due centesimi al dibattito, ho creato un modulo angolare puro (senza jQuery), e funzionerà anche con gli hash url contenenti dati. (ad es. #/this/is/path?this=is&some=data)

È sufficiente aggiungere il modulo come dipendenza e auto-activea uno degli antenati del menu. Come questo:

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

E il modulo è simile al seguente:

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

(Ovviamente puoi semplicemente usare la parte direttiva)

Vale anche la pena notare che questo non funziona per gli hash vuoti (ad esempio example.com/#o solo example.com) che deve avere almeno example.com/#/o solo example.com#/. Ma questo succede automaticamente con ngResource e simili.

Ed ecco il violino: http://jsfiddle.net/gy2an/8/


1
Ottima soluzione, tuttavia non ha funzionato al caricamento della pagina iniziale, solo su locationChange mentre l'app è attiva. Ho aggiornato il tuo frammento per gestirlo.
Jerry,

@Jarek: grazie! Hai implementato le tue modifiche. Non ho avuto problemi con questo personalmente, ma la tua soluzione sembra una buona soluzione stabile per coloro che dovrebbero imbattersi in questo problema.
Pylinux il

2
Ora ho creato un repository Github per richieste pull se qualcun altro ha qualche buona idea: github.com/Karl-Gustav/autoActive
Pylinux

Ho appena corretto alcuni altri bug che si verificano se si utilizza ng-href .. Questo è disponibile qui: github.com/Karl-Gustav/autoActive/pull/3
Blake Niemyjski

Sarebbe bello se questo script ti consentisse di specificare un percorso in modo da poterlo attivare altri elementi.
Blake Niemyjski,

22

Nel mio caso ho risolto questo problema creando un semplice controller responsabile della navigazione

angular.module('DemoApp')
  .controller('NavigationCtrl', ['$scope', '$location', function ($scope, $location) {
    $scope.isCurrentPath = function (path) {
      return $location.path() == path;
    };
  }]);

E semplicemente aggiungendo ng-class all'elemento in questo modo:

<ul class="nav" ng-controller="NavigationCtrl">
  <li ng-class="{ active: isCurrentPath('/') }"><a href="#/">Home</a></li>
  <li ng-class="{ active: isCurrentPath('/about') }"><a href="#/about">About</a></li>
  <li ng-class="{ active: isCurrentPath('/contact') }"><a href="#/contact">Contact</a></li>
</ul>

14

Per gli utenti di AngularUI Router:

<a ui-sref-active="active" ui-sref="app">

E questo posizionerà una activeclasse sull'oggetto selezionato.


2
Questa è una direttiva ui-router e non funziona se si utilizza il router integrato, noto anche come ngRoute. Detto questo, l'Ui-router è fantastico.
moljac024,

D'accordo, ho dimenticato di menzionare inizialmente che si trattava di una soluzione solo per ui-router.
frankie4fingers,

13

Esiste una ng-classdirettiva che lega variabili e classe css. Accetta anche l'oggetto (className vs coppie valore bool).

Ecco l'esempio http://plnkr.co/edit/SWZAqj


Grazie, ma questo non funzionerà con percorsi come: /test1/blahblaho lo farà?
Andriy Drozdyuk,

Quindi stai dicendo che active: activePath=='/test1'restituisce automaticamente un "attivo" è il percorso inizia con la stringa data? È una specie di operatore predefinito o regex?
Andriy Drozdyuk,

Spiacenti, non credo di aver capito correttamente il tuo requisito. Ecco la mia nuova ipotesi, vuoi sia evidenziare il link 'test1' E il link 'test1 / blahblah' evidenziato quando il percorso è 'test1 / blahblah'. "Ho ragione? Se questo è il requisito, hai ragione che la mia soluzione non funziona lavoro
Tosh,

3
Ecco il plnkr aggiornato: plnkr.co/edit/JI5DtK (che soddisfa il requisito ipotizzato) per mostrare semplicemente una soluzione alternativa.
Tosh,

Vedo quello che hai fatto là. Ma non sono un fan dei ==controlli ripetuti nell'html.
Andriy Drozdyuk,

13

La risposta di @ Renan-tomal-fernandes è buona, ma ha bisogno di un paio di miglioramenti per funzionare correttamente. Com'era, avrebbe sempre rilevato il collegamento alla home page (/) come attivato, anche se ti trovassi in un'altra sezione.

Quindi l'ho migliorato un po ', ecco il codice. Lavoro con Bootstrap, quindi la parte attiva è <li>nell'elemento anziché nel file<a> .

controllore

$scope.getClass = function(path) {
    var cur_path = $location.path().substr(0, path.length);
    if (cur_path == path) {
        if($location.path().substr(0).length > 1 && path.length == 1 )
            return "";
        else
            return "active";
    } else {
        return "";
    }
}

Modello

<div class="nav-collapse collapse">
  <ul class="nav">
    <li ng-class="getClass('/')"><a href="#/">Home</a></li>
    <li ng-class="getClass('/contents/')"><a href="#/contests/">Contents</a></li>
    <li ng-class="getClass('/data/')"><a href="#/data/">Your data</a></li>
  </ul>
</div>

10

Ecco la soluzione che mi è venuta in mente dopo aver letto alcuni degli eccellenti suggerimenti di cui sopra. Nella mia situazione particolare, stavo cercando di utilizzare il componente delle schede Bootstrap come mio menu, ma non volevo usare la versione dell'interfaccia angolare di questo perché voglio che le schede agissero come un menu, in cui ogni scheda è in grado di aggiungere segnalibri, anziché le schede che fungono da navigazione per una singola pagina. (Vedi http://angular-ui.github.io/bootstrap/#/tabs se sei interessato a come appare la versione Angular-UI delle schede bootstrap).

Mi è piaciuta molto la risposta di KFIS sulla creazione di una propria direttiva per gestirla, tuttavia mi è sembrato complicato avere una direttiva che doveva essere inserita in ogni singolo link. Quindi ho creato la mia direttiva angolare che viene invece posizionata una volta sul ul. Nel caso in cui qualcun altro stia cercando di fare la stessa cosa, ho pensato di pubblicarlo qui, anche se, come ho detto, anche molte delle soluzioni sopra funzionano. Questa è una soluzione leggermente più complessa per quanto riguarda javascript, ma crea un componente riutilizzabile con markup minimo.

Ecco il javascript per la direttiva e il provider di route per ng:view:

var app = angular.module('plunker', ['ui.bootstrap']).
  config(['$routeProvider', function($routeProvider) {
    $routeProvider.
        when('/One', {templateUrl: 'one.html'}).
        when('/Two', {templateUrl: 'two.html'}).
        when('/Three', {templateUrl: 'three.html'}).
        otherwise({redirectTo: '/One'});
  }]).
  directive('navTabs', ['$location', function(location) {
    return {
        restrict: 'A',
        link: function(scope, element) {
            var $ul = $(element);
            $ul.addClass("nav nav-tabs");

            var $tabs = $ul.children();
            var tabMap = {};
            $tabs.each(function() {
              var $li = $(this);
              //Substring 1 to remove the # at the beginning (because location.path() below does not return the #)
              tabMap[$li.find('a').attr('href').substring(1)] = $li;
            });

            scope.location = location;
            scope.$watch('location.path()', function(newPath) {
                $tabs.removeClass("active");
                tabMap[newPath].addClass("active");
            });
        }

    };

 }]);

Quindi nel tuo HTML semplicemente:

<ul nav-tabs>
  <li><a href="#/One">One</a></li>
  <li><a href="#/Two">Two</a></li>
  <li><a href="#/Three">Three</a></li>
</ul>
<ng:view><!-- Content will appear here --></ng:view>

Ecco il plunker per questo: http://plnkr.co/edit/xwGtGqrT7kWoCKnGDHYN?p=preview .


9

Puoi implementarlo in modo molto semplice, ecco un esempio:

<div ng-controller="MenuCtrl">
  <ul class="menu">
    <li ng-class="menuClass('home')"><a href="#home">Page1</a></li>
    <li ng-class="menuClass('about')"><a href="#about">Page2</a></li>
  </ul>

</div>

E il tuo controller dovrebbe essere questo:

app.controller("MenuCtrl", function($scope, $location) {
  $scope.menuClass = function(page) {
    var current = $location.path().substring(1);
    return page === current ? "active" : "";
  };
});


4

Ho avuto un problema simile con il menu situato al di fuori dell'ambito del controller. Non sono sicuro se questa è la soluzione migliore o consigliata ma questo è ciò che ha funzionato per me. Ho aggiunto quanto segue alla mia configurazione dell'app:

var app = angular.module('myApp');

app.run(function($rootScope, $location){
  $rootScope.menuActive = function(url, exactMatch){
    if (exactMatch){
      return $location.path() == url;
    }
    else {
      return $location.path().indexOf(url) == 0;
    }
  }
});

Quindi nella vista ho:

<li><a href="/" ng-class="{true: 'active'}[menuActive('/', true)]">Home</a></li>
<li><a href="/register" ng-class="{true: 'active'}[menuActive('/register')]">
<li>...</li>

Um ... questo sembra più complicato della risposta accettata. Potresti descriverne il vantaggio rispetto a quello?
Andriy Drozdyuk,

1
Ne avrai bisogno nello scenario quando il tuo menu è al di fuori di ng-view. Il controller di visualizzazione non avrà accesso a tutto ciò che è esterno, quindi ho usato $ rootScope per abilitare la comunicazione. Se il tuo menu è all'interno di ng-view, allora non ci sono vantaggi nell'utilizzare questa soluzione.
sig.

4

Uso della versione angolare 6 con Bootstrap 4.1

Sono stato in grado di farlo come visto di seguito.

Nell'esempio seguente, quando l'URL visualizza '/ contact', il bootstrap attivo viene quindi aggiunto al tag html. Quando l'URL cambia, viene quindi rimosso.

<ul>
<li class="nav-item" routerLink="/contact" routerLinkActive="active">
    <a class="nav-link" href="/contact">Contact</a>
</li>
</ul>

Questa direttiva consente di aggiungere una classe CSS a un elemento quando la route del collegamento diventa attiva.

Maggiori informazioni sul sito Web angolare


3

Usando una direttiva (dato che qui stiamo manipolando il DOM), il seguente è probabilmente il più vicino a fare le cose in "modo angolare":

$scope.timeFilters = [
  {'value':3600,'label':'1 hour'},
  {'value':10800,'label':'3 hours'},
  {'value':21600,'label':'6 hours'},
  {'value':43200,'label':'12 hours'},
  {'value':86400,'label':'24 hours'},
  {'value':604800,'label':'1 week'}
]

angular.module('whatever', []).directive('filter',function(){
return{
    restrict: 'A',
    template: '<li ng-repeat="time in timeFilters" class="filterItem"><a ng-click="changeTimeFilter(time)">{{time.label}}</a></li>',
    link: function linkFn(scope, lElement, attrs){

        var menuContext = attrs.filter;

        scope.changeTimeFilter = function(newTime){
          scope.selectedtimefilter = newTime;

        }

        lElement.bind('click', function(cevent){
            var currentSelection = angular.element(cevent.srcElement).parent();
            var previousSelection = scope[menuContext];

            if(previousSelection !== currentSelection){
                if(previousSelection){
                    angular.element(previousSelection).removeClass('active')
                }
                scope[menuContext] = currentSelection;

                scope.$apply(function(){
                    currentSelection.addClass('active');
                })
            }
        })
    }
}
})

Quindi il tuo HTML sarebbe simile a:

<ul class="dropdown-menu" filter="times"></ul>

Interessante. Ma menu-itemsembra ridondante su ogni linea. Forse allegare un attributo ulall'elemento (es. <ul menu>) Sarebbe meglio, ma non sono sicuro che ciò sia possibile.
Andriy Drozdyuk,

Appena aggiornato con una versione più recente - anziché un elenco non ordinato statico, ora sto usando il menu a discesa Boostrap come elenco di selezione.
Wesley Hales,

Questo sembra il più simile angolare idiomatico. Sembra adattarsi ai consigli forniti su stackoverflow.com/questions/14994391/… ed evita di duplicare il percorso nella vista, nell'href e nella classe ng.
raccolta fondi

2

L'ho fatto così:

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

myApp.directive('trackActive', function($location) {
    function link(scope, element, attrs){
        scope.$watch(function() {
            return $location.path();
        }, function(){
            var links = element.find('a');
            links.removeClass('active');
            angular.forEach(links, function(value){
                var a = angular.element(value);
                if (a.attr('href') == '#' + $location.path() ){
                    a.addClass('active');
                }
            });
        });
    }
    return {link: link};
});

Ciò consente di disporre di collegamenti in una sezione con direttiva track-active:

<nav track-active>
     <a href="#/">Page 1</a>
     <a href="#/page2">Page 2</a>
     <a href="#/page3">Page 3</a>
</nav>

Questo approccio mi sembra molto più pulito di altri.

Inoltre, se stai usando jQuery, puoi renderlo molto più ordinato perché jQlite ha solo il supporto selettore di base. Una versione molto più pulita con jquery inclusa prima dell'inclusione angolare sarebbe simile a questa:

myApp.directive('trackActive', function($location) {
    function link(scope, element, attrs){
        scope.$watch(function() {
            return $location.path();
        }, function(){
            element.find('a').removeClass('active').find('[href="#'+$location.path()+'"]').addClass('active');
        });
    }
    return {link: link};
});

Ecco un jsFiddle


2

La mia soluzione a questo problema, utilizzare route.currentnel modello angolare.

Dato che hai il /taskspercorso da evidenziare nel tuo menu, puoi aggiungere la tua proprietà menuItemai percorsi dichiarati dal tuo modulo:

$routeProvider.
  when('/tasks', {
    menuItem: 'TASKS',
    templateUrl: 'my-templates/tasks.html',
    controller: 'TasksController'
  );

Quindi nel modello tasks.htmlè possibile utilizzare la seguente ng-classdirettiva:

<a href="app.html#/tasks" 
    ng-class="{active : route.current.menuItem === 'TASKS'}">Tasks</a>

A mio avviso, questo è molto più pulito di tutte le soluzioni proposte.


1

Ecco un'estensione sulla direttiva kfis che ho fatto per consentire diversi livelli di corrispondenza dei percorsi. In sostanza ho trovato la necessità di abbinare i percorsi URL fino a una certa profondità, poiché la corrispondenza esatta non consente l'annidamento e i reindirizzamenti di stato predefiniti. Spero che questo ti aiuti.

    .directive('selectedLink', ['$location', function(location) {
    return {
        restrict: 'A',
        scope:{
            selectedLink : '='
            },
        link: function(scope, element, attrs, controller) {
            var level = scope.selectedLink;
            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) {
                var i=0;
                p = path.split('/');
                n = newPath.split('/');
                for( i ; i < p.length; i++) { 
                    if( p[i] == 'undefined' || n[i] == 'undefined' || (p[i] != n[i]) ) break;
                    }

                if ( (i-1) >= level) {
                    element.addClass("selected");
                    } 
                else {
                    element.removeClass("selected");
                    }
                });
            }

        };
    }]);

Ed ecco come utilizzo il link

<nav>
    <a href="#/info/project/list"  selected-link="2">Project</a>
    <a href="#/info/company/list" selected-link="2">Company</a>
    <a href="#/info/person/list"  selected-link="2">Person</a>
</nav>

Questa direttiva corrisponderà al livello di profondità specificato nel valore dell'attributo per la direttiva. Significa solo che può essere usato altrove molte volte.


1

Ecco un'altra direttiva per evidenziare i collegamenti attivi.

Caratteristiche principali:

  • Funziona bene con href che contiene espressioni angolari dinamiche
  • Compatibile con la navigazione hash-bang
  • Compatibile con Bootstrap in cui la classe attiva deve essere applicata al padre non al collegamento stesso
  • Consente di rendere attivo il collegamento se è attivo un percorso nidificato
  • Consente di disattivare il collegamento se non è attivo

Codice:

.directive('activeLink', ['$location', 
function($location) {
    return {
        restrict: 'A',
        link: function(scope, elem, attrs) {
            var path = attrs.activeLink ? 'activeLink' : 'href';
            var target = angular.isDefined(attrs.activeLinkParent) ? elem.parent() : elem;
            var disabled = angular.isDefined(attrs.activeLinkDisabled) ? true : false;
            var nested = angular.isDefined(attrs.activeLinkNested) ? true : false;

            function inPath(needle, haystack) {
                var current = (haystack == needle);
                if (nested) {
                    current |= (haystack.indexOf(needle + '/') == 0);
                }

                return current;
            }

            function toggleClass(linkPath, locationPath) {
                // remove hash prefix and trailing slashes
                linkPath = linkPath ? linkPath.replace(/^#!/, '').replace(/\/+$/, '') : '';
                locationPath = locationPath.replace(/\/+$/, '');

                if (linkPath && inPath(linkPath, locationPath)) {
                    target.addClass('active');
                    if (disabled) {
                        target.removeClass('disabled');
                    }
                } else {
                    target.removeClass('active');
                    if (disabled) {
                        target.addClass('disabled');
                    }
                }
            }

            // watch if attribute value changes / evaluated
            attrs.$observe(path, function(linkPath) {
                toggleClass(linkPath, $location.path());
            });

            // watch if location changes
            scope.$watch(
                function() {
                    return $location.path(); 
                }, 
                function(newPath) {
                    toggleClass(attrs[path], newPath);
                }
            );
        }
    };
}
]);

Uso:

Esempio semplice con espressione angolare, diciamo $ scope.var = 2 , quindi il collegamento sarà attivo se location è / url / 2 :

<a href="#!/url/{{var}}" active-link>

Esempio Bootstrap, parent li otterrà la classe attiva:

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

Esempio con URL nidificati, il collegamento sarà attivo se è attivo qualsiasi URL nidificato (ad es. / Url / 1 , / url / 2 , url / 1/2 / ... )

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

Esempio complesso, il collegamento punta a un url ( / url1 ) ma sarà attivo se ne viene selezionato un altro ( / url2 ):

<a href="#!/url1" active-link="#!/url2" active-link-nested>

Esempio con collegamento disabilitato, se non è attivo avrà la classe 'disabilitato' :

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

Tutti gli attributi active-link- * possono essere utilizzati in qualsiasi combinazione, quindi potrebbero essere implementate condizioni molto complesse.


1

Se vuoi i collegamenti per la direttiva in un wrapper piuttosto che selezionare ogni singolo collegamento (rende più facile guardare l'ambito in Batarang), funziona anche abbastanza bene:

  angular.module("app").directive("navigation", [
    "$location", function($location) {
      return {
        restrict: 'A',
        scope: {},
        link: function(scope, element) {
          var classSelected, navLinks;

          scope.location = $location;

          classSelected = 'selected';

          navLinks = element.find('a');

          scope.$watch('location.path()', function(newPath) {
            var el;
            el = navLinks.filter('[href="' + newPath + '"]');

            navLinks.not(el).closest('li').removeClass(classSelected);
            return el.closest('li').addClass(classSelected);
          });
        }
      };
    }
  ]);

Il markup sarebbe solo:

    <nav role="navigation" data-navigation>
        <ul>
            <li><a href="/messages">Messages</a></li>
            <li><a href="/help">Help</a></li>
            <li><a href="/details">Details</a></li>
        </ul>
    </nav>

Dovrei anche menzionare che sto usando jQuery "full-fat" in questo esempio, ma puoi facilmente modificare ciò che ho fatto con il filtro e così via.


1

Ecco i miei due centesimi, funziona benissimo.

NOTA: questo non corrisponde alle pagine secondarie (che è ciò di cui avevo bisogno).

Visualizza:

<a ng-class="{active: isCurrentLocation('/my-path')}"  href="/my-path" >
  Some link
</a>

controller:

// make sure you inject $location as a dependency

$scope.isCurrentLocation = function(path){
    return path === $location.path()
}

1

Secondo la risposta di @kfis, sono i commenti, e la mia raccomandazione, la direttiva finale come di seguito:

.directive('activeLink', ['$location', function (location) {
    return {
      restrict: 'A',
      link: function(scope, element, attrs, controller) {
        var clazz = attrs.activeLink;        
        var path = attrs.href||attrs.ngHref;
        path = path.substring(1); //hack because path does not return including hashbang
        scope.location = location;
        scope.$watch('window.location.href', function () {
          var newPath = (window.location.pathname + window.location.search).substr(1);
          if (path === newPath) {
            element.addClass(clazz);
          } else {
            element.removeClass(clazz);
          }
        });
      }
    };
  }]);

ed ecco come sarebbe usato in 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>

successivamente styling con css:

.active { color: red; }

1

Per coloro che usano ui-router, la mia risposta è in qualche modo simile a quella di Ender2050, ma preferisco farlo tramite il test del nome dello stato:

$scope.isActive = function (stateName) {
  var active = (stateName === $state.current.name);
  return active;
};

HTML corrispondente:

<ul class="nav nav-sidebar">
    <li ng-class="{ active: isActive('app.home') }"><a ui-sref="app.home">Dashboard</a></li>
    <li ng-class="{ active: isActive('app.tiles') }"><a ui-sref="app.tiles">Tiles</a></li>
</ul>

1

Nessuno dei suddetti suggerimenti della direttiva mi è stato utile. Se hai una navbar bootstrap come questa

<ul class="nav navbar-nav">
    <li><a ng-href="#/">Home</a></li>
    <li><a ng-href="#/about">About</a></li>
  ...
</ul>

(che potrebbe essere un $ yo angularavvio) poi si desidera aggiungere .activealla genitore <li> lista di classe elementi, non l'elemento stesso; vale a dire <li class="active">..</li>. Quindi ho scritto questo:

.directive('setParentActive', ['$location', function($location) {
  return {
    restrict: 'A',
    link: function(scope, element, attrs, controller) {
      var classActive = attrs.setParentActive || 'active',
          path = attrs.ngHref.replace('#', '');
      scope.location = $location;
      scope.$watch('location.path()', function(newPath) {
        if (path == newPath) {
          element.parent().addClass(classActive);
        } else {
          element.parent().removeClass(classActive);
        }
      })
    }
  }
}])

utilizzo set-parent-active; .activeè predefinito, quindi non è necessario impostarlo

<li><a ng-href="#/about" set-parent-active>About</a></li>

e l' <li>elemento principale sarà .activequando il collegamento è attivo. Per usare una .activeclasse alternativa come .highlight, semplicemente

<li><a ng-href="#/about" set-parent-active="highlight">About</a></li>

Avevo provato scope. $ On ("$ routeChangeSuccess", funzione (evento, corrente, precedente) {applyActiveClass ();}); ma funziona solo quando si fa clic sul collegamento e non "al caricamento della pagina" (facendo clic sul pulsante di aggiornamento). guardare la posizione ha funzionato per me
visto il

0

La cosa più importante per me era non cambiare affatto il codice predefinito di bootstrap. Qui è il mio controller di menu che cerca le opzioni di menu e quindi aggiunge il comportamento che vogliamo.

file: header.js
function HeaderCtrl ($scope, $http, $location) {
  $scope.menuLinkList = [];
  defineFunctions($scope);
  addOnClickEventsToMenuOptions($scope, $location);
}

function defineFunctions ($scope) {
  $scope.menuOptionOnClickFunction = function () {
    for ( var index in $scope.menuLinkList) {
      var link = $scope.menuLinkList[index];
      if (this.hash === link.hash) {
        link.parentElement.className = 'active';
      } else {
        link.parentElement.className = '';
      }
    }
  };
}

function addOnClickEventsToMenuOptions ($scope, $location) {
  var liList = angular.element.find('li');
  for ( var index in liList) {
    var liElement = liList[index];
    var link = liElement.firstChild;
    link.onclick = $scope.menuOptionOnClickFunction;
    $scope.menuLinkList.push(link);
    var path = link.hash.replace("#", "");
    if ($location.path() === path) {
      link.parentElement.className = 'active';
    }
  }
}

     <script src="resources/js/app/header.js"></script>
 <div class="navbar navbar-fixed-top" ng:controller="HeaderCtrl">
    <div class="navbar-inner">
      <div class="container-fluid">
        <button type="button" class="btn btn-navbar" data-toggle="collapse" data-target=".nav-collapse">
          <span class="icon-bar"></span> <span class="icon-bar"></span> 
<span     class="icon-bar"></span>
        </button>
        <a class="brand" href="#"> <img src="resources/img/fom-logo.png"
          style="width: 80px; height: auto;">
        </a>
        <div class="nav-collapse collapse">
          <ul class="nav">
            <li><a href="#/platforms">PLATFORMS</a></li>
            <li><a href="#/functionaltests">FUNCTIONAL TESTS</a></li>
          </ul> 
        </div>
      </div>
    </div>
  </div>

0

ha avuto lo stesso problema. Ecco la mia soluzione :

.directive('whenActive',
  [
    '$location',
    ($location)->
      scope: true,
      link: (scope, element, attr)->
        scope.$on '$routeChangeSuccess', 
          () ->
            loc = "#"+$location.path()
            href = element.attr('href')
            state = href.indexOf(loc)
            substate = -1

            if href.length > 3
              substate = loc.indexOf(href)
            if loc.length is 2
              state = -1

            #console.log "Is Loc: "+loc+" in Href: "+href+" = "+state+" and Substate = "+substate

            if state isnt -1 or substate isnt -1
              element.addClass 'selected'
              element.parent().addClass 'current-menu-item'
            else if href is '#' and loc is '#/'
              element.addClass 'selected'
              element.parent().addClass 'current-menu-item'
            else
              element.removeClass 'selected'
              element.parent().removeClass 'current-menu-item'
  ])

0

Ho appena scritto una direttiva per questo.

Uso:

<ul class="nav navbar-nav">
  <li active><a href="#/link1">Link 1</a></li>
  <li active><a href="#/link2">Link 2</a></li>
</ul>

Implementazione:

angular.module('appName')
  .directive('active', function ($location, $timeout) {
    return {
      restrict: 'A',
      link: function (scope, element, attrs) {
        // Whenever the user navigates to a different page...
        scope.$on('$routeChangeSuccess', function () {
          // Defer for other directives to load first; this is important
          // so that in case other directives are used that this directive
          // depends on, such as ng-href, the href is evaluated before
          // it's checked here.
          $timeout(function () {
            // Find link inside li element
            var $link = element.children('a').first();

            // Get current location
            var currentPath = $location.path();

            // Get location the link is pointing to
            var linkPath = $link.attr('href').split('#').pop();

            // If they are the same, it means the user is currently
            // on the same page the link would point to, so it should
            // be marked as such
            if (currentPath === linkPath) {
              $(element).addClass('active');
            } else {
              // If they're not the same, a li element that is currently
              // marked as active needs to be "un-marked"
              element.removeClass('active');
            }
          });
        });
      }
    };
  });

test:

'use strict';

describe('Directive: active', function () {

  // load the directive's module
  beforeEach(module('appName'));

  var element,
      scope,
      location,
      compile,
      rootScope,
      timeout;

  beforeEach(inject(function ($rootScope, $location, $compile, $timeout) {
    scope = $rootScope.$new();
    location = $location;
    compile = $compile;
    rootScope = $rootScope;
    timeout = $timeout;
  }));

  describe('with an active link', function () {
    beforeEach(function () {
      // Trigger location change
      location.path('/foo');
    });

    describe('href', function () {
      beforeEach(function () {
        // Create and compile element with directive; note that the link
        // is the same as the current location after the location change.
        element = angular.element('<li active><a href="#/foo">Foo</a></li>');
        element = compile(element)(scope);

        // Broadcast location change; the directive waits for this signal
        rootScope.$broadcast('$routeChangeSuccess');

        // Flush timeout so we don't have to write asynchronous tests.
        // The directive defers any action using a timeout so that other
        // directives it might depend on, such as ng-href, are evaluated
        // beforehand.
        timeout.flush();
      });

      it('adds the class "active" to the li', function () {
        expect(element.hasClass('active')).toBeTruthy();
      });
    });

    describe('ng-href', function () {
      beforeEach(function () {
        // Create and compile element with directive; note that the link
        // is the same as the current location after the location change;
        // however this time with an ng-href instead of an href.
        element = angular.element('<li active><a ng-href="#/foo">Foo</a></li>');
        element = compile(element)(scope);

        // Broadcast location change; the directive waits for this signal
        rootScope.$broadcast('$routeChangeSuccess');

        // Flush timeout so we don't have to write asynchronous tests.
        // The directive defers any action using a timeout so that other
        // directives it might depend on, such as ng-href, are evaluated
        // beforehand.
        timeout.flush();
      });

      it('also works with ng-href', function () {
        expect(element.hasClass('active')).toBeTruthy();
      });
    });
  });

  describe('with an inactive link', function () {
    beforeEach(function () {
      // Trigger location change
      location.path('/bar');

      // Create and compile element with directive; note that the link
      // is the NOT same as the current location after the location change.
      element = angular.element('<li active><a href="#/foo">Foo</a></li>');
      element = compile(element)(scope);

      // Broadcast location change; the directive waits for this signal
      rootScope.$broadcast('$routeChangeSuccess');

      // Flush timeout so we don't have to write asynchronous tests.
      // The directive defers any action using a timeout so that other
      // directives it might depend on, such as ng-href, are evaluated
      // beforehand.
      timeout.flush();
    });

    it('does not add the class "active" to the li', function () {
      expect(element.hasClass('active')).not.toBeTruthy();
    });
  });

  describe('with a formerly active link', function () {
    beforeEach(function () {
      // Trigger location change
      location.path('/bar');

      // Create and compile element with directive; note that the link
      // is the same as the current location after the location change.
      // Also not that the li element already has the class "active".
      // This is to make sure that a link that is active right now will
      // not be active anymore when the user navigates somewhere else.
      element = angular.element('<li class="active" active><a href="#/foo">Foo</a></li>');
      element = compile(element)(scope);

      // Broadcast location change; the directive waits for this signal
      rootScope.$broadcast('$routeChangeSuccess');

      // Flush timeout so we don't have to write asynchronous tests.
      // The directive defers any action using a timeout so that other
      // directives it might depend on, such as ng-href, are evaluated
      // beforehand.
      timeout.flush();
    });

    it('removes the "active" class from the li', function () {
      expect(element.hasClass('active')).not.toBeTruthy();
    });
  });
});

0

La strada:

$routeProvider.when('/Account/', { templateUrl: '/Home/Account', controller: 'HomeController' });

Il menu HTML:

<li id="liInicio" ng-class="{'active':url=='account'}">

Il controller:

angular.module('Home').controller('HomeController', function ($scope, $http, $location) {
    $scope.url = $location.url().replace(/\//g, "").toLowerCase();
...

Il problema che ho riscontrato qui è che la voce di menu è attiva solo quando viene caricata l'intera pagina. Quando viene caricata la vista parziale, il menu non cambia. Qualcuno sa perché succede?


0
$scope.getClass = function (path) {
return String(($location.absUrl().split('?')[0]).indexOf(path)) > -1 ? 'active' : ''
}


<li class="listing-head" ng-class="getClass('/v/bookings')"><a href="/v/bookings">MY BOOKING</a></li>
<li class="listing-head" ng-class="getClass('/v/fleets')"><a href="/v/fleets">MY FLEET</a></li>
<li class="listing-head" ng-class="getClass('/v/adddriver')"><a href="/v/adddriver">ADD DRIVER</a></li>
<li class="listing-head" ng-class="getClass('/v/bookings')"><a href="/v/invoice">INVOICE</a></li>
<li class="listing-head" ng-class="getClass('/v/profile')"><a href="/v/profile">MY PROFILE</a></li>
<li class="listing-head"><a href="/v/logout">LOG OUT</a></li>

0

Ho trovato la soluzione più semplice. solo per confrontare indexOf in HTML

var myApp = angular.module('myApp', []);

myApp.run(function($rootScope) {
    $rootScope.$on("$locationChangeStart", function(event, next, current) { 
         $rootScope.isCurrentPath = $location.path();  
    });
});



<li class="{{isCurrentPath.indexOf('help')>-1 ? 'active' : '' }}">
<a href="/#/help/">
          Help
        </a>
</li>
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.