Wie markiere ich einen aktuellen Menüpunkt?

205

Hilft AngularJS in irgendeiner Weise beim Festlegen einer activeKlasse für den Link für die aktuelle Seite?

Ich stelle mir vor, dass dies auf magische Weise geschieht, aber ich kann es nicht finden.

Mein Menü sieht aus wie:

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

und ich habe Controller für jeden von ihnen in meinen Routen: TasksControllerund ActionsController.

Aber ich kann keine Möglichkeit finden, die "aktive" Klasse an die aLinks zu den Controllern zu binden .

Irgendwelche Hinweise?

Andriy Drozdyuk
quelle

Antworten:

265

zu sehen

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

am Controller

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

Damit hat der Aufgabenlink die aktive Klasse in einer beliebigen URL, die mit '/asks' beginnt (z. B. '/ task / 1 / reports').

Renan Tomal Fernandes
quelle
4
Dies würde am Ende sowohl mit "/" als auch mit "/ irgendetwas" übereinstimmen oder wenn Sie mehrere Menüelemente mit ähnlichen URLs haben, wie "/ test", "/ test / this", "/ test / this / path", wenn Sie eingeschaltet wären / test würde alle diese Optionen hervorheben.
Ben Lesh
3
Ich habe dies geändert in if ($ location.path () == path) und y path ist "/ blah" etc
Tim
113
Ich bevorzuge die Notation ngClass="{active: isActive('/tasks')}, bei isActive()der ein Boolescher Wert zurückgegeben wird, da er den Controller und das Markup / Styling entkoppelt.
Ed Hinchliffe
6
Nur für den Fall, dass sich jemand über den Code wundert, der nicht verdoppelt werden soll, wenn der Pfad "/" ist, ist dies (Entschuldigung für die Formatierung): $ scope.getClass = function (path) {if ($ location.path (). substr (0, path.length) == path) {if (path == "/" && $ location.path () == "/") {return "active"; } else if (path == "/") {return ""; } return "active"} else {return ""}}
1
EdHinchliffe hat bereits darauf hingewiesen, dass dies Markup und Logik vermischt. Dies führt auch zu einer Duplizierung des Pfads und kann daher zu Fehlern beim Kopieren und Einfügen führen. Ich habe festgestellt, dass der Direktivenansatz von @kfis, obwohl mehr Zeilen, wiederverwendbarer ist und das Markup sauberer hält.
A. Murray
86

Ich schlage vor, eine Direktive für einen Link zu verwenden.

Aber es ist noch nicht perfekt. Pass auf die Hashbangs auf;)

Hier ist das Javascript für die Direktive:

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

und hier ist, wie es in HTML verwendet werden würde:

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

danach Styling mit CSS:

.active { color: red; }
kfis
quelle
Ich bin mir nicht sicher, was Sie unter "Achten Sie auf Hashbangs" verstehen. Es scheint, als würde es immer funktionieren. Könnten Sie ein Gegenbeispiel liefern?
Andriy Drozdyuk
7
Wenn Sie versuchen, Bootstrap zu verwenden und basierend auf dem Hash eines a's href innerhalb eines li festlegen müssen, verwenden Sie var path = $(element).children("a")[0].hash.substring(1); . Dies wird für einen Stil wie<li active-link="active"><a href="#/dashboard">Dashboard</a></li>
Dave
2
ich würde ändern scope.$watch('location.path()', function(newPath) { für scope.$on('$locationChangeStart', function(){.
Sanfilippopablo
2
Wenn Sie ng-href verwenden, ändern Sie einfach: var path = attrs.href; zu var path = attrs.href||attrs.ngHref;
William Neely
Wenn Sie Bootstrap verwenden und die aktive Klasse auf setzen müssen <li>, können Sie element.addClass(clazz);zuelement.parent().addClass(clazz);
JamesRLamar wechseln
47

Hier ist ein einfacher Ansatz, der gut mit Angular funktioniert.

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

In Ihrem AngularJS-Controller:

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

Dieser Thread hat eine Reihe anderer ähnlicher Antworten.

Wie setze ich die aktive Klasse der Bootstrap-Navigationsleiste mit Angular JS?

Ender2050
quelle
1
Entfernen Sie die Variable, da dies nicht erforderlich ist. Geben Sie einfach das Vergleichsergebnis zurück. return viewLocation === $location.path()
Afarazit
33

Um meine zwei Cent in die Debatte einzubeziehen, habe ich ein reines Winkelmodul (kein jQuery) erstellt, das auch mit Hash-URLs funktioniert, die Daten enthalten. (z.B#/this/is/path?this=is&some=data )

Sie fügen das Modul einfach als Abhängigkeit und auto-activezu einem der Vorfahren des Menüs hinzu. So was:

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

Und das Modul sieht so aus:

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

(Sie können natürlich auch nur den Direktiven-Teil verwenden)

Es ist auch erwähnenswert, dass dies nicht für leere Hashes (z. B. example.com/#oder nur example.com) funktioniert , die mindestens example.com/#/oder nur vorhanden sein müssenexample.com#/ . Dies geschieht jedoch automatisch mit ngResource und dergleichen.

Und hier ist die Geige: http://jsfiddle.net/gy2an/8/

Pylinux
quelle
1
Tolle Lösung, aber es hat beim ersten Laden der Seite nicht funktioniert, nur bei locationChange, während die App live ist. Ich habe Ihr Snippet aktualisiert, um damit umzugehen .
Jerry
@ Jarek: Danke! Haben Ihre Änderungen umgesetzt. Ich hatte persönlich keine Probleme damit, aber Ihre Lösung scheint eine gute stabile Lösung für diejenigen zu sein, die auf dieses Problem stoßen sollten.
Pylinux
2
Ich habe jetzt ein Github-Repo für Pull-Anfragen erstellt, wenn jemand andere gute Ideen hat: github.com/Karl-Gustav/autoActive
Pylinux
Ich habe nur ein paar weitere Fehler behoben, die auftreten würden, wenn Sie ng-href verwenden würden. Dies ist hier zu finden: github.com/Karl-Gustav/autoActive/pull/3
Blake Niemyjski
Es wäre schön, wenn Sie mit diesem Skript einen Pfad angeben könnten, damit andere Elemente aktiviert werden.
Blake Niemyjski
22

In meinem Fall habe ich dieses Problem gelöst, indem ich einen einfachen Controller erstellt habe, der für die Navigation verantwortlich ist

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

Und indem Sie dem Element einfach die folgende ng-Klasse hinzufügen:

<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>
Djamel
quelle
14

Für AngularUI Router-Benutzer:

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

Dadurch wird eine activeKlasse für das ausgewählte Objekt platziert.

frankie4fingers
quelle
2
Dies ist eine UI-Router-Direktive und funktioniert nicht, wenn Sie den integrierten Router, auch bekannt als ngRoute, verwenden. Das heißt, UI-Router ist super.
Moljac024
Ich stimme zu, ich habe ursprünglich vergessen zu erwähnen, dass es sich nur um eine UI-Router-Lösung handelt.
frankie4fingers
13

Da ist ein ng-class Direktive, die Variable und CSS-Klasse bindet. Es akzeptiert auch das Objekt (className vs bool value pair).

Hier ist das Beispiel http://plnkr.co/edit/SWZAqj

Tosh
quelle
Danke, aber das funktioniert nicht mit Pfaden wie: /test1/blahblahoder?
Andriy Drozdyuk
Wollen Sie damit sagen, dass active: activePath=='/test1'automatisch ein "aktiver" Pfad zurückgegeben wird, der mit der angegebenen Zeichenfolge beginnt? Ist dies eine Art vordefinierter Operator oder Regex?
Andriy Drozdyuk
Entschuldigung, ich glaube nicht, dass ich Ihre Anforderung richtig verstanden habe. Hier ist meine neue Vermutung: Sie möchten, dass sowohl der Link 'test1' als auch der Link 'test1 / blahblah' hervorgehoben werden, wenn die Route 'test1 / blahblah' lautet. "Bin ich richtig? Wenn dies die Anforderung ist, haben Sie Recht, dass meine Lösung dies nicht tut Arbeit.
Tosh
3
Hier ist das aktualisierte plnkr: plnkr.co/edit/JI5DtK (das die vermutete Anforderung erfüllt), um nur eine alternative Lösung zu zeigen.
Tosh
Ich sehe, was du dort getan hast. Aber ich bin kein Fan von wiederholten ==Überprüfungen im HTML.
Andriy Drozdyuk
13

Die Antwort von @ Renan-tomal-fernandes ist gut, aber es waren einige Verbesserungen erforderlich, um richtig zu funktionieren. So wie es war, wurde der Link zur Startseite (/) immer als ausgelöst erkannt, selbst wenn Sie sich in einem anderen Abschnitt befanden.

Also habe ich es ein bisschen verbessert, hier ist der Code. Ich arbeite mit Bootstrap, also befindet sich der aktive Teil im <li>Element anstelle des <a>.

Regler

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

Vorlage

<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>
holographix
quelle
10

Hier ist die Lösung, die ich gefunden habe, nachdem ich einige der oben genannten hervorragenden Vorschläge gelesen habe. In meiner speziellen Situation habe ich versucht, die Bootstrap-Registerkartenkomponente als Menü zu verwenden, wollte jedoch nicht die Angular-UI-Version davon verwenden, da die Registerkarten als Menü fungieren sollen, in dem jede Registerkarte mit Lesezeichen versehen werden kann. anstatt der Registerkarten, die als Navigation für eine einzelne Seite dienen. (Siehe http://angular-ui.github.io/bootstrap/#/tabs, wenn Sie daran interessiert sind, wie die Angular-UI-Version von Bootstrap-Registerkarten aussieht.)

Die Antwort von kfis, eine eigene Direktive zu erstellen, um dies zu handhaben, hat mir sehr gut gefallen, aber es schien umständlich, eine Direktive zu haben, die auf jedem einzelnen Link platziert werden musste. Also habe ich meine eigene Angular-Direktive erstellt, die stattdessen einmal auf der platziert wirdul . Nur für den Fall, dass jemand anderes versucht, dasselbe zu tun, dachte ich, ich würde es hier posten, obwohl, wie gesagt, viele der oben genannten Lösungen auch funktionieren. Dies ist eine etwas komplexere Lösung, was das Javascript betrifft, aber es wird eine wiederverwendbare Komponente mit minimalem Markup erstellt.

Hier ist das Javascript für die Richtlinie und der Routenanbieter für 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");
            });
        }

    };

 }]);

Dann in Ihrem HTML Sie einfach:

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

Hier ist der Plunker dafür: http://plnkr.co/edit/xwGtGqrT7kWoCKnGDHYN?p=preview .

Corinnaerin
quelle
9

Sie können dies sehr einfach implementieren, hier ein Beispiel:

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

Und Ihr Controller sollte dies sein:

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

Ich hatte ein ähnliches Problem mit dem Menü außerhalb des Controller-Bereichs. Ich bin mir nicht sicher, ob dies die beste oder eine empfohlene Lösung ist, aber das hat bei mir funktioniert. Ich habe meiner App-Konfiguration Folgendes hinzugefügt:

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

Dann habe ich in der Ansicht:

<li><a href="/" ng-class="{true: 'active'}[menuActive('/', true)]">Home</a></li>
<li><a href="/register" ng-class="{true: 'active'}[menuActive('/register')]">
<li>...</li>
mrt
quelle
Ähm ... das scheint komplizierter als die akzeptierte Antwort. Könnten Sie den Vorteil davon gegenüber diesem beschreiben?
Andriy Drozdyuk
1
Sie benötigen es im Szenario, wenn sich Ihr Menü außerhalb der ng-Ansicht befindet. Der View Controller hat keinen Zugriff auf etwas außerhalb, daher habe ich $ rootScope verwendet, um die Kommunikation zu ermöglichen. Befindet sich Ihr Menü in der ng-Ansicht, hat die Verwendung dieser Lösung keinen Vorteil für Sie.
Mrt
4

Verwenden von Angular Version 6 mit Bootstrap 4.1

Ich konnte es wie unten gezeigt erledigen.

Wenn im folgenden Beispiel die URL "/ contact" anzeigt, wird der aktive Bootstrap zum HTML-Tag hinzugefügt. Wenn sich die URL ändert, wird sie entfernt.

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

Mit dieser Anweisung können Sie einem Element eine CSS-Klasse hinzufügen, wenn die Route des Links aktiv wird.

Lesen Sie mehr auf der Angular-Website

Jeacovy Gayle
quelle
3

Unter Verwendung einer Direktive (da wir hier DOM-Manipulationen durchführen) ist das Folgende wahrscheinlich am nächsten daran, Dinge auf "eckige Weise" zu tun:

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

Dann würde Ihr HTML wie folgt aussehen:

<ul class="dropdown-menu" filter="times"></ul>
Wesley Hales
quelle
Interessant. Scheint menu-itemaber in jeder Zeile überflüssig. Vielleicht wäre es besser, ein Attribut an ein ulElement anzuhängen (z. B. <ul menu>), aber ich bin mir nicht sicher, ob dies möglich wäre.
Andriy Drozdyuk
Gerade mit einer neueren Version aktualisiert - anstelle einer statischen ungeordneten Liste verwende ich jetzt das Dropdown-Menü Boostrap als Auswahlliste.
Wesley Hales
Dies scheint dem idiomatischen Winkel am ähnlichsten zu sein. Es scheint mit den Ratschlägen unter stackoverflow.com/questions/14994391/… übereinzustimmen und vermeidet das Duplizieren des Pfads in der Ansicht, in der href und in der ng-Klasse.
Fundead
2

Ich habe es so gemacht:

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

Auf diese Weise können Sie Links in einem Abschnitt mit einer Track-Active-Direktive haben:

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

Dieser Ansatz scheint mir viel sauberer als andere.

Wenn Sie jQuery verwenden, können Sie es auch viel übersichtlicher gestalten, da jQlite nur grundlegende Selektorunterstützung bietet. Eine viel sauberere Version mit JQuery vor Angular Include würde folgendermaßen aussehen:

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

Hier ist eine jsFiddle

konsumer
quelle
2

Meine Lösung für dieses Problem verwenden Sie route.currentin der Winkelschablone.

Da Sie die /tasksRoute in Ihrem Menü hervorheben müssen, können Sie menuItemden von Ihrem Modul deklarierten Routen Ihre eigene Eigenschaft hinzufügen :

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

Dann können Sie in Ihrer Vorlage tasks.htmlfolgende ng-classAnweisung verwenden:

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

Meiner Meinung nach ist dies viel sauberer als alle vorgeschlagenen Lösungen.

François Maturel
quelle
1

Hier ist eine Erweiterung der kfis-Direktive, die ich gemacht habe, um verschiedene Ebenen der Pfadanpassung zu ermöglichen. Im Wesentlichen stellte ich fest, dass URL-Pfade bis zu einer bestimmten Tiefe abgeglichen werden müssen, da eine genaue Übereinstimmung keine Verschachtelung und Standardstatusumleitungen zulässt. Hoffe das hilft.

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

        };
    }]);

Und so benutze ich den 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>

Diese Direktive entspricht der im Attributwert für die Direktive angegebenen Tiefenstufe. Bedeutet nur, dass es um ein Vielfaches an anderer Stelle verwendet werden kann.

pkbyron
quelle
1

Hier ist noch eine weitere Anweisung, um aktive Links hervorzuheben.

Hauptmerkmale:

  • Funktioniert gut mit href, das dynamische Winkelausdrücke enthält
  • Kompatibel mit Hash-Bang-Navigation
  • Kompatibel mit Bootstrap, bei dem die aktive Klasse auf das übergeordnete Element angewendet werden soll, nicht auf den Link selbst
  • Ermöglicht das Aktivieren der Verknüpfung, wenn ein verschachtelter Pfad aktiv ist
  • Ermöglicht das Deaktivieren der Verknüpfung, wenn diese nicht aktiv ist

Code:

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

Verwendung:

Ein einfaches Beispiel mit einem Winkelausdruck, sagen wir $ scope.var = 2 , dann ist der Link aktiv, wenn der Speicherort / url / 2 ist :

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

Bootstrap-Beispiel, Eltern-Li erhält aktive Klasse:

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

Beispiel mit verschachtelten URLs: Der Link ist aktiv, wenn eine verschachtelte URL aktiv ist (z. B. / url / 1 , / url / 2 , url / 1/2 / ... ).

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

Komplexes Beispiel: Link verweist auf eine URL ( / url1 ), ist jedoch aktiv, wenn eine andere ausgewählt ist ( / url2 ):

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

Beispiel mit deaktiviertem Link: Wenn er nicht aktiv ist, hat er die Klasse 'disabled' :

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

Alle Active-Link- * -Attribute können in beliebiger Kombination verwendet werden, sodass sehr komplexe Bedingungen implementiert werden können.

Eugene Fidelin
quelle
1

Wenn Sie die Links für die Direktive in einem Wrapper haben möchten, anstatt jeden einzelnen Link auszuwählen (erleichtert das Betrachten des Bereichs in Batarang), funktioniert dies auch ziemlich gut:

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

Markup wäre nur:

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

Ich sollte auch erwähnen, dass ich in diesem Beispiel 'vollfettes' jQuery verwende, aber Sie können leicht ändern, was ich mit der Filterung gemacht habe und so weiter.

marksyzm
quelle
1

Hier sind meine zwei Cent, das funktioniert gut.

HINWEIS: Dies stimmt nicht mit den Kinderseiten überein (was ich brauchte).

Aussicht:

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

Regler:

// make sure you inject $location as a dependency

$scope.isCurrentLocation = function(path){
    return path === $location.path()
}
Justus Romijn
quelle
1

Laut der Antwort von @kfis sind es Kommentare und meine Empfehlung, die endgültige Richtlinie wie folgt:

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

und hier ist, wie es in HTML verwendet werden würde:

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

danach Styling mit CSS:

.active { color: red; }
John_J
quelle
1

Für diejenigen, die einen UI-Router verwenden, ist meine Antwort etwas ähnlich wie die von Ender2050, aber ich bevorzuge dies über das Testen des Statusnamens:

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

entsprechender HTML:

<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>
GONeale
quelle
1

Keiner der oben genannten Richtlinienvorschläge war für mich nützlich. Wenn Sie eine Bootstrap-Navigationsleiste wie diese haben

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

(das könnte ein $ yo angularStart sein) Dann möchten Sie .activeder Liste der übergeordneten <li> Elementklassen hinzufügen , nicht dem Element selbst; dh <li class="active">..</li>. Also habe ich folgendes geschrieben:

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

Nutzung set-parent-active; .activeist die Standardeinstellung, muss also nicht eingestellt werden

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

und das übergeordnete <li>Element ist, .activewenn der Link aktiv ist. Um eine alternative .activeKlasse wie .highlighteinfach zu verwenden

<li><a ng-href="#/about" set-parent-active="highlight">About</a></li>
davidkonrad
quelle
Ich hatte den Bereich ausprobiert. $ On ("$ routeChangeSuccess", Funktion (Ereignis, aktuell, vorher) {applyActiveClass ();}); Dies funktioniert jedoch nur, wenn auf den Link geklickt wird und nicht "Beim Laden der Seite" (Klicken auf die Schaltfläche "Aktualisieren"). Das Anschauen des Ortes hat für mich
funktioniert
0

Für mich war es am wichtigsten, den Bootstrap-Standardcode überhaupt nicht zu ändern. Hier ist es mein Menü-Controller, der nach Menüoptionen sucht und dann das gewünschte Verhalten hinzufügt.

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>
user2599258
quelle
0

hatte das gleiche Problem. Hier ist meine Lösung :

.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'
  ])
Naxmeify
quelle
0

Ich habe gerade eine Anweisung dafür geschrieben.

Verwendung:

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

Implementierung:

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

Tests:

'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();
    });
  });
});
weltschmerz
quelle
0

Die Route:

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

Das Menü HTML:

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

Der Controller:

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

Das Problem, das ich hier gefunden habe, ist, dass der Menüpunkt nur aktiv ist, wenn die ganze Seite geladen ist. Wenn die Teilansicht geladen ist, ändert sich das Menü nicht. Weiß jemand warum es passiert?

Herr D MX
quelle
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>
Ashish Gupta
quelle
0

Ich habe die einfachste Lösung gefunden. Nur um indexOf in HTML zu vergleichen

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>
NishantVerma.Me
quelle