Jak podświetlić bieżący element menu?

205

Czy AngularJS pomaga w jakikolwiek sposób ustawić activeklasę w linku do bieżącej strony?

Wyobrażam sobie, że jest to magiczny sposób, ale nie mogę tego znaleźć.

Moje menu wygląda następująco:

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

i mam kontrolery dla każdego z nich na moich trasach: TasksControlleri ActionsController.

Ale nie mogę znaleźć sposobu na powiązanie „aktywnej” klasy z ałączami do kontrolerów.

Jakieś wskazówki?

Andrij Drozdyuk
źródło

Odpowiedzi:

265

na widoku

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

na kontrolerze

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

Dzięki temu link do zadań będzie miał aktywną klasę w dowolnym adresie URL rozpoczynającym się od „/ zadania” (np. „/ Zadania / 1 / raporty”)

Renan Tomal Fernandes
źródło
4
Spowodowałoby to dopasowanie zarówno „/” i „/ cokolwiek” lub jeśli masz wiele pozycji menu z podobnymi adresami URL, takich jak „/ test”, „/ test / this”, „/ test / this / path” / test, podświetli wszystkie te opcje.
Ben Lesh
3
Zmieniłem to na if ($ location.path () == ścieżka) i, y ścieżka to „/ bla” itd.
Tim
113
Wolę notację ngClass="{active: isActive('/tasks')}, w której isActive()zwraca wartość logiczną, ponieważ oddziela kontroler i znaczniki / styl.
Ed Hinchliffe
6
Na wypadek, gdyby ktoś zastanawiał się, czy kod nie podwaja się, jeśli ścieżka to „/”, to jest to (przepraszam za formatowanie): $ 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 wskazał już, że łączy to znaczniki i logikę. Prowadzi to również do duplikowania ścieżki i może być podatne na błędy kopiowania i wklejania. Przekonałem się, że podejście dyrektywy @kfis, chociaż więcej wierszy, jest bardziej użyteczne i utrzymuje znaczniki w czystości.
A. Murray,
86

Sugeruję użycie dyrektywy dotyczącej linku.

Ale jeszcze nie jest idealny. Uważaj na hashbangi;)

Oto javascript dla dyrektywy:

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

a oto, jak miałby być użyty w 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>

następnie stylizacja za pomocą css:

.active { color: red; }
kfis
źródło
Nie jestem pewien, co masz na myśli przez „uważaj na hashbangi”. Wygląda na to, że zawsze zadziała. Czy możesz podać kontrprzykład?
Andriy Drozdyuk
7
Jeśli próbujesz użyć Bootstrap i chcesz ustawić na podstawie skrótu href a w li, użyj var path = $(element).children("a")[0].hash.substring(1);. To zadziała dla stylu typu<li active-link="active"><a href="#/dashboard">Dashboard</a></li>
Dave
2
Zmieniłbym się scope.$watch('location.path()', function(newPath) {na scope.$on('$locationChangeStart', function(){.
sanfilippopablo
2
Jeśli używasz ng-href, po prostu zmień: var path = attrs.href; na var path = attrs.href||attrs.ngHref;
William Neely
Jeśli używasz Bootstrap i chcesz umieścić aktywną klasę <li>, możesz zmienić element.addClass(clazz);naelement.parent().addClass(clazz);
JamesRLamar,
47

Oto proste podejście, które działa dobrze w 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>

W twoim kontrolerze AngularJS:

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

Wątek zawiera wiele innych podobnych odpowiedzi.

Jak ustawić aktywną klasę paska startowego paska nawigacyjnego za pomocą Angular JS?

Ender2050
źródło
1
Usuń zmienną, ponieważ jest niepotrzebna. Po prostu zwróć wynik porównania. return viewLocation === $location.path()
afarazit
33

Aby dodać moje dwa centy do debaty, stworzyłem czysty moduł kątowy (bez jQuery), i będzie on również działał z adresami skrótu zawierającymi dane. (np. #/this/is/path?this=is&some=data)

Po prostu dodajesz moduł jako zależność i auto-activedo jednego z przodków menu. Lubię to:

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

Moduł wygląda następująco:

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

(Oczywiście możesz po prostu użyć części dotyczącej dyrektywy)

Warto również zauważyć, że nie działa to dla pustych skrótów (np. example.com/#Lub po prostu example.com), musi mieć przynajmniej example.com/#/lub tylko example.com#/. Ale dzieje się to automatycznie z ngResource i podobnymi.

A oto skrzypce: http://jsfiddle.net/gy2an/8/

Pylinux
źródło
1
Świetne rozwiązanie, jednak nie działało przy pierwszym ładowaniu strony, tylko w lokalizacji Zmień, gdy aplikacja jest uruchomiona. Zaktualizowałem Twój fragment, aby sobie z tym poradzić.
Jerry
@Jarek: Dzięki! Wdrożyłem twoje zmiany. Nie miałem z tym problemów osobiście, ale twoje rozwiązanie wydaje się być dobrym, stabilnym rozwiązaniem dla tych, którzy powinni napotkać ten problem.
Pylinux
2
Utworzyłem repozytorium Github dla żądań ściągnięcia, jeśli ktoś ma jakieś dobre pomysły: github.com/Karl-Gustav/autoActive
Pylinux
Właśnie naprawiłem kilka innych błędów, które występowałyby, gdybyś używał ng-href .. Można go znaleźć tutaj: github.com/Karl-Gustav/autoActive/pull/3
Blake Niemyjski,
Byłoby miło, gdyby ten skrypt pozwolił ci określić ścieżkę, abyś mógł aktywować inne elementy.
Blake Niemyjski,
22

W moim przypadku rozwiązałem ten problem, tworząc prosty kontroler odpowiedzialny za nawigację

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

I po prostu dodając klasę ng do elementu tak:

<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
źródło
14

Dla użytkowników routera AngularUI :

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

I to umieści activeklasę na wybranym obiekcie.

frankie4fingers
źródło
2
Jest to dyrektywa ui-router i nie działa, jeśli używasz wbudowanego routera, czyli ngRoute. To powiedziawszy, interfejs użytkownika jest niesamowity.
moljac024
Zgadzam się, zapomniałem pierwotnie wspomnieć, że było to rozwiązanie tylko dla routera.
frankie4fingers,
13

Istnieje ng-classdyrektywa, która wiąże zmienną i klasę css. Akceptuje również obiekt (pary nazwa-klasy vs pary wartości bool).

Oto przykład: http://plnkr.co/edit/SWZAqj

Bzdury
źródło
Dzięki, ale to nie zadziała ze ścieżkami takimi jak: /test1/blahblahczy to będzie?
Andriy Drozdyuk
Więc mówisz, że active: activePath=='/test1'automatycznie zwraca „aktywne”, czy ścieżka zaczyna się od podanego ciągu? Czy jest to jakiś predefiniowany operator lub wyrażenie regularne?
Andriy Drozdyuk
Niestety nie sądzę, że poprawnie zrozumiałem twoje wymagania. Oto moje nowe przypuszczenie, że chcesz wyróżnić zarówno link „test1”, jak i link „test1 / blahblah”, gdy trasa to „test1 / blahblah”. „Czy mam rację? Jeśli jest to wymóg, masz rację, że moje rozwiązanie nie praca
Tosh
3
Oto zaktualizowany plik plnkr: plnkr.co/edit/JI5DtK (który spełnia domniemane wymagania), aby pokazać alternatywne rozwiązanie.
Tosh
Widzę, co tu zrobiłeś. Ale nie jestem fanem powtarzających się ==kontroli w html.
Andriy Drozdyuk
13

odpowiedź od @ Renan-Tomal-Fernandes jest dobra, ale potrzebne kilka ulepszeń działało poprawnie. W obecnej postaci zawsze wykrywał link do strony głównej (/) po uruchomieniu, nawet jeśli byłeś w innej sekcji.

Więc trochę to poprawiłem, oto kod. Pracuję z Bootstrap, więc aktywna część jest w <li>elemencie zamiast w<a> .

Kontroler

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

Szablon

<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
źródło
10

Oto rozwiązanie, które wymyśliłem po przeczytaniu kilku doskonałych sugestii powyżej. W mojej szczególnej sytuacji próbowałem użyć komponentu kart Bootstrap jako menu, ale nie chciałem używać wersji Angular-UI, ponieważ chcę, aby karty działały jako menu, w którym każda karta może zawierać zakładki, zamiast kart działających jako nawigacja dla pojedynczej strony. (Patrz http://angular-ui.github.io/bootstrap/#/tabs jeśli jesteś zainteresowany tym, jak wygląda wersja Angular-UI kart bootstrap).

Naprawdę podobała mi się odpowiedź kfis na temat stworzenia własnej dyrektywy, aby sobie z tym poradzić, jednak wydawało się kłopotliwe mieć dyrektywę, która musi być umieszczona na każdym linku. Stworzyłem więc własną dyrektywę Angular, która zamiast tego jest umieszczana raz naul . Na wypadek, gdyby ktokolwiek próbował zrobić to samo, pomyślałem, że opublikuję to tutaj, chociaż, jak powiedziałem, wiele z powyższych rozwiązań również działa. Jest to nieco bardziej złożone rozwiązanie, jeśli chodzi o javascript, ale tworzy komponent wielokrotnego użytku z minimalnym znacznikiem.

Oto javascript dla dyrektywy i dostawca trasy dla 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");
            });
        }

    };

 }]);

Następnie w swoim html po prostu:

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

Oto plunker: http://plnkr.co/edit/xwGtGqrT7kWoCKnGDHYN?p=preview .

corinnaerin
źródło
9

Możesz to zaimplementować bardzo prosto, oto przykład:

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

Twój kontroler powinien wyglądać tak:

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

Miałem podobny problem z menu znajdującym się poza zasięgiem kontrolera. Nie jestem pewien, czy jest to najlepsze, czy zalecane rozwiązanie, ale to właśnie dla mnie zadziałało. Dodałem następujące elementy do mojej konfiguracji aplikacji:

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

Następnie w widoku mam:

<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
źródło
Hmm ... wydaje się to bardziej skomplikowane niż zaakceptowana odpowiedź. Czy możesz opisać przewagę tego nad tym?
Andriy Drozdyuk
1
Będziesz go potrzebować w scenariuszu, gdy twoje menu jest poza widokiem ng. Kontroler widoku nie będzie miał dostępu do niczego, co jest na zewnątrz, więc użyłem $ rootScope, aby umożliwić komunikację. Jeśli twoje menu znajduje się w widoku ng, korzystanie z tego rozwiązania nie przynosi żadnych korzyści.
mrt
4

Używanie Angulara Wersja 6 z Bootstrap 4.1

Udało mi się to zrobić tak, jak pokazano poniżej.

W poniższym przykładzie, gdy URL widzi „/ contact”, aktywny bootstrap jest następnie dodawany do znacznika HTML. Kiedy adres URL się zmienia, jest on następnie usuwany.

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

Ta dyrektywa pozwala dodać klasę CSS do elementu, gdy trasa łącza staje się aktywna.

Przeczytaj więcej na stronie Angular

Jeacovy Gayle
źródło
3

Korzystając z dyrektywy (ponieważ tutaj wykonujemy manipulację DOM), prawdopodobnie najbardziej zbliżamy się do wykonywania rzeczy „pod kątem”:

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

Wtedy twój HTML wyglądałby następująco:

<ul class="dropdown-menu" filter="times"></ul>
Wesley Hales
źródło
Ciekawy. Ale menu-itemwydaje się zbędny na każdej linii. Być może dołączenie atrybutu do ulelementu (np. <ul menu>) Byłoby lepsze, ale nie jestem pewien, czy byłoby to możliwe.
Andriy Drozdyuk
Właśnie zaktualizowana o nowszą wersję - zamiast statycznej nieuporządkowanej listy, teraz używam menu rozwijanego Boostrap jako listy wyboru.
Wesley Hales,
To wydaje się najbardziej podobne do idiomatycznego kąta. Wydaje się, że pasuje do rad podanych na stackoverflow.com/questions/14994391 /... i unika powielania ścieżki w widoku, w href i klasie ng.
fundead
2

Zrobiłem tak:

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

Dzięki temu możesz mieć linki w sekcji, która ma dyrektywę track-active:

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

To podejście wydaje mi się znacznie czystsze niż inne.

Ponadto, jeśli używasz jQuery, możesz uczynić go o wiele bardziej przyjaznym, ponieważ jQlite ma tylko podstawową obsługę selektora. O wiele czystsza wersja z jquery zawartą przed angular zawierałaby się tak:

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

Oto jsFiddle

konsumer
źródło
2

Moje rozwiązanie tego problemu, użyj route.currentw szablonie kątowym.

Ponieważ masz /taskstrasę do wyróżnienia w menu, możesz dodać własną właściwość menuItemdo tras zadeklarowanych przez moduł:

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

Następnie w swoim szablonie tasks.htmlmożesz użyć następującej ng-classdyrektywy:

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

Moim zdaniem jest to znacznie czystsze niż wszystkie proponowane rozwiązania.

François Maturel
źródło
1

Oto rozszerzenie dyrektywy kfis, które zrobiłem, aby umożliwić różne poziomy dopasowania ścieżki. Zasadniczo znalazłem potrzebę dopasowania ścieżek URL do określonej głębokości, ponieważ dokładne dopasowanie nie pozwala na zagnieżdżanie i domyślne przekierowania stanu. Mam nadzieję że to pomoże.

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

        };
    }]);

A oto jak korzystam z linku

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

Ta dyrektywa będzie odpowiadać poziomowi głębokości określonemu w wartości atrybutu dyrektywy. Oznacza to, że można go wielokrotnie używać w innym miejscu.

pkbyron
źródło
1

Oto kolejna dyrektywa podkreślająca aktywne linki.

Kluczowe cechy:

  • Działa dobrze z href, który zawiera dynamiczne wyrażenia kątowe
  • Kompatybilny z nawigacją hash-bang
  • Kompatybilny z Bootstrap, gdzie klasa nadrzędna powinna być stosowana do nadrzędnego li, a nie do samego łącza
  • Pozwala aktywować link, jeśli aktywna jest jakakolwiek zagnieżdżona ścieżka
  • Pozwala wyłączyć link, jeśli nie jest aktywny

Kod:

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

Stosowanie:

Prosty przykład z wyrażeniem kątowym, powiedzmy $ scope.var = 2 , wtedy link będzie aktywny, jeśli lokalizacja to / url / 2 :

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

Przykład bootstrapu, rodzic li uzyska aktywną klasę:

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

Przykład z zagnieżdżonymi adresami URL, link będzie aktywny, jeśli jakikolwiek zagnieżdżony adres URL jest aktywny (tj. / Url / 1 , / url / 2 , url / 1/2 / ... )

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

Złożony przykład, link wskazuje na jeden adres URL ( / url1 ), ale będzie aktywny, jeśli zostanie wybrany inny adres ( / url2 ):

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

Przykład z wyłączonym linkiem, jeśli nie jest aktywny, będzie miał klasę „wyłączoną” :

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

Wszystkie atrybuty active-link- * mogą być używane w dowolnej kombinacji, więc można zastosować bardzo złożone warunki.

Eugene Fidelin
źródło
1

Jeśli chcesz, aby linki do dyrektywy były umieszczone w opakowaniu, a nie wybieranie poszczególnych linków (ułatwia przeglądanie zakresu w Batarang), działa to również całkiem dobrze:

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

Znaczniki będą po prostu:

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

Powinienem również wspomnieć, że w tym przykładzie używam jQuery z pełną zawartością tłuszczu, ale możesz łatwo zmienić to, co zrobiłem z filtrowaniem i tak dalej.

marksyzm
źródło
1

Oto moje dwa centy, działa dobrze.

UWAGA: Nie pasuje to do stron podrzędnych (czego potrzebowałem).

Widok:

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

Kontroler:

// make sure you inject $location as a dependency

$scope.isCurrentLocation = function(path){
    return path === $location.path()
}
Justus Romijn
źródło
1

Zgodnie z odpowiedzią @kfis są to komentarze i, moim zdaniem, ostateczna dyrektywa jak poniżej:

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

a oto, jak miałby być użyty w 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>

następnie stylizacja za pomocą css:

.active { color: red; }
John_J
źródło
1

Dla tych, którzy używają interfejsu użytkownika, moja odpowiedź jest nieco podobna do Ender2050, ale wolę to zrobić poprzez testowanie nazwy stanu:

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

odpowiadający 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
źródło
1

Żadna z powyższych sugestii dotyczących dyrektywy nie była dla mnie przydatna. Jeśli masz pasek nawigacyjny bootstrap taki jak ten

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

(może to być $ yo angularstartup), a następnie chcesz dodać .activedo listy klas elementów nadrzędnych <li> , a nie sam element; tj <li class="active">..</li>. Więc napisałem to:

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

użycie set-parent-active; .activejest domyślne, więc nie trzeba go ustawiać

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

a <li>element nadrzędny będzie, .activegdy łącze jest aktywne. Aby użyć alternatywnej .activeklasy .highlight, po prostu

<li><a ng-href="#/about" set-parent-active="highlight">About</a></li>
dawidkonrad
źródło
Próbowałem zakresu. $ On ("$ routeChangeSuccess", funkcja (zdarzenie, bieżące, poprzednie) {ApplyActiveClass ();}); ale działa to tylko po kliknięciu linku, a nie „wczytywaniu strony” (kliknięciu przycisku odświeżania). oglądanie lokalizacji zadziałało dla mnie
piła
0

Najważniejsze dla mnie było to, aby nie zmieniać w ogóle domyślnego kodu bootstrap. Oto mój kontroler menu, który wyszukuje opcje menu, a następnie dodaje pożądane zachowanie.

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>
użytkownik2599258
źródło
0

miał ten sam problem. Oto moje rozwiązanie :

.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
źródło
0

Właśnie napisałem w tym celu dyrektywę.

Stosowanie:

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

Realizacja:

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

Testy:

'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
źródło
0

Trasa:

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

Menu html:

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

Kontroler:

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

Problem, który tu znalazłem, polega na tym, że element menu jest aktywny tylko po załadowaniu całej strony. Po załadowaniu widoku częściowego menu się nie zmienia. Ktoś wie, dlaczego tak się dzieje?

Pan D MX
źródło
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
źródło
0

Znalazłem najłatwiejsze rozwiązanie. aby porównać indexOf w 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>
NishantVerma.Me
źródło