AngularJS aide-t-il de quelque manière que ce soit à définir une classe active
sur le lien de la page en cours?
J'imagine qu'il y a une façon magique de faire cela, mais je n'arrive pas à le trouver.
Mon menu ressemble à:
<ul>
<li><a class="active" href="/tasks">Tasks</a>
<li><a href="/actions">Tasks</a>
</ul>
et j'ai des contrôleurs pour chacun d'eux dans mes routes: TasksController
et ActionsController
.
Mais je ne peux pas trouver un moyen de lier la classe "active" sur les liens a
aux contrôleurs.
Des allusions?
en vue
<a ng-class="getClass('/tasks')" href="/tasks">Tasks</a>
sur le contrôleur
$scope.getClass = function (path) {
return ($location.path().substr(0, path.length) === path) ? 'active' : '';
}
Avec cela, le lien tasks aura la classe active dans n'importe quelle URL commençant par '/tasks'(e.g.'/Tasks/1/reports ')
Je suggère d'utiliser une directive sur un lien.
Mais ce n'est pas encore parfait. Attention aux hashbangs;)
Voici le javascript pour la directive:
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);
}
});
}
};
}]);
et voici comment il serait utilisé en HTML:
<div ng-app="link">
<a href="#/one" active-link="active">One</a>
<a href="#/two" active-link="active">One</a>
<a href="#" active-link="active">home</a>
</div>
après le style avec css:
.active { color: red; }
Voici une approche simple qui fonctionne bien avec 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>
Dans votre contrôleur AngularJS:
$scope.isActive = function (viewLocation) {
var active = (viewLocation === $location.path());
return active;
};
Ce fil a un certain nombre d'autres réponses similaires.
Comment définir la classe active bootstrap navbar avec Angular JS?
Juste pour ajouter mes deux cents dans le débat, j'ai créé un module purement angulaire (pas de jQuery), qui fonctionnera également avec des URL de hachage contenant des données. (par exemple, #/this/is/path?this=is&some=data
)
Vous ajoutez simplement le module en tant que dépendance et auto-active
à l'un des ancêtres du menu. Comme ça:
<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>
Et le module ressemble à ceci:
(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);
}
}
}]);
}());
(Vous pouvez bien sûr simplement utiliser la partie directive)
Il convient également de noter que cela ne fonctionne pas pour les hachages vides (par exemple, example.com/#
ou simplement example.com
), il doit comporter au moins example.com/#/
ou seulement example.com#/
. Mais cela se produit automatiquement avec ngResource, etc.
Et voici le violon: http://jsfiddle.net/gy2an/8/
Dans mon cas, j'ai résolu ce problème en créant un contrôleur simple responsable de la navigation.
angular.module('DemoApp')
.controller('NavigationCtrl', ['$scope', '$location', function ($scope, $location) {
$scope.isCurrentPath = function (path) {
return $location.path() == path;
};
}]);
Et en ajoutant simplement ng-class à l'élément comme ceci:
<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>
Pour les utilisateurs de AngularUI Router:
<a ui-sref-active="active" ui-sref="app">
Et cela placera une classe active
sur l'objet sélectionné.
Il existe une directive ng-class
, qui lie variable et css class . Elle accepte également l’objet (paires className vs bool value).
Voici l'exemple, http://plnkr.co/edit/SWZAqj
Le answer de @ Renan-tomal-fernandes est bon, mais quelques améliorations sont nécessaires pour fonctionner correctement. même si vous étiez dans une autre section.
Donc, je l’ai un peu amélioré, voici le code . Je travaille avec Bootstrap donc la partie active est dans l’élément <li>
au lieu du <a>
.
Manette
$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 "";
}
}
Modèle
<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>
Voici la solution que j'ai trouvée après avoir lu certaines des excellentes suggestions ci-dessus. Dans ma situation particulière, j’essayais d’utiliser Le composant Onglets de Bootstrap en tant que mon menu, mais je ne voulais pas utiliser la version Angular-UI de cela car je voulais que les onglets agissent comme un menu, où chaque onglet compatible avec les signets, plutôt que les onglets agissant comme une navigation pour une seule page. (Voir http://angular-ui.github.io/bootstrap/#/tabs si vous êtes intéressé par ce à quoi ressemble la version Angular-UI des onglets de bootstrap).
J'ai vraiment aimé la réponse de Kfis sur la création de votre propre directive pour gérer cela, mais il semblait fastidieux d'avoir une directive qui devait être placée sur chaque lien. J'ai donc créé ma propre directive angulaire qui est placée à la place une fois sur la variable ul
. Juste au cas où quelqu'un essaierait de faire la même chose, j'ai pensé l'afficher ici, bien que, comme je l'ai dit, nombre des solutions ci-dessus fonctionnent également. C'est une solution légèrement plus complexe en ce qui concerne le javascript, mais elle crée un composant réutilisable avec un balisage minimal.
Voici le javascript de la directive et le fournisseur de route pour 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");
});
}
};
}]);
Alors dans votre html vous simplement:
<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>
Voici le plunker pour cela: http://plnkr.co/edit/xwGtGqrT7kWoCKnGDHYN?p=preview .
Vous pouvez implémenter ceci très simplement, voici un exemple:
<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>
Et votre contrôleur devrait être ceci:
app.controller("MenuCtrl", function($scope, $location) {
$scope.menuClass = function(page) {
var current = $location.path().substring(1);
return page === current ? "active" : "";
};
});
utilisez la directive ui-sref-active de angular-ui-router https://github.com/angular-ui/ui-router/wiki/Quick-Reference#statename
<ul>
<li ui-sref-active="active" class="item">
<a href ui-sref="app.user({user: 'bilbobaggins'})">@bilbobaggins</a>
</li>
<!-- ... -->
</ul>
J'ai eu un problème similaire avec menu situé à l'extérieur la portée du contrôleur. Pas sûr que ce soit la meilleure solution ou celle recommandée, mais c'est ce qui a fonctionné pour moi. J'ai ajouté les éléments suivants à la configuration de mon application:
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;
}
}
});
Alors dans la vue j'ai:
<li><a href="/" ng-class="{true: 'active'}[menuActive('/', true)]">Home</a></li>
<li><a href="/register" ng-class="{true: 'active'}[menuActive('/register')]">
<li>...</li>
En utilisant une directive (puisque nous faisons ici des manipulations du DOM), ce qui suit est probablement le plus proche de faire les choses de la "manière angulaire":
$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');
})
}
})
}
}
})
Votre code HTML ressemblerait alors à:
<ul class="dropdown-menu" filter="times"></ul>
Ma solution à ce problème, utilisez route.current
dans le modèle angulaire.
Comme vous devez sélectionner la route /tasks
dans votre menu, vous pouvez ajouter votre propre propriété menuItem
aux routes déclarées par votre module:
$routeProvider.
when('/tasks', {
menuItem: 'TASKS',
templateUrl: 'my-templates/tasks.html',
controller: 'TasksController'
);
Ensuite, dans votre modèle tasks.html
, vous pouvez utiliser la directive ng-class
suivante:
<a href="app.html#/tasks"
ng-class="{active : route.current.menuItem === 'TASKS'}">Tasks</a>
À mon avis, cela est beaucoup plus propre que toutes les solutions proposées.
Je l'ai fait comme ça:
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};
});
Cela vous permet d’avoir des liens dans une section qui a une directive track-active:
<nav track-active>
<a href="#/">Page 1</a>
<a href="#/page2">Page 2</a>
<a href="#/page3">Page 3</a>
</nav>
Cette approche me semble beaucoup plus propre que les autres.
De plus, si vous utilisez jQuery, vous pouvez le rendre beaucoup plus simple car jQlite ne prend en charge que le sélecteur de base. Une version beaucoup plus propre avec jquery inclus avant include angular ressemblerait à ceci:
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};
});
Selon la réponse de @kfis, il s’agit de commentaires, et je recommande la directive finale ci-dessous:
.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);
}
});
}
};
}]);
et voici comment il serait utilisé en HTML:
<div ng-app="link"> <a href="#/one" active-link="active">One</a> <a href="#/two" active-link="active">One</a> <a href="#" active-link="active">home</a> </div>
après le style avec css:
.active { color: red; }
Voici encore une autre directive pour mettre en évidence les liens actifs.
.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);
}
);
}
};
}
]);
Exemple simple avec une expression angulaire, disons $ scope.var = 2 , alors le lien sera actif si location est/url/2 :
<a href="#!/url/{{var}}" active-link>
Exemple de bootstrap, le parent li obtiendra la classe active:
<li>
<a href="#!/url" active-link active-link-parent>
</li>
Exemple avec des URL imbriquées, le lien sera actif si une URL imbriquée est active (par exemple,/url/1 ,/url/2 , url/1/2/... )
<a href="#!/url" active-link active-link-nested>
Exemple complexe, le lien pointe vers une URL (/url1 ) mais sera actif si une autre est sélectionnée (/url2 ):
<a href="#!/url1" active-link="#!/url2" active-link-nested>
Exemple avec un lien désactivé, s'il n'est pas actif, il aura 'disabled' class:
<a href="#!/url" active-link active-link-disabled>
Tous les attributs active-link- * peuvent être utilisés dans n'importe quelle combinaison, ce qui permet de réaliser des conditions très complexes.
Pour ceux qui utilisent ui-router, ma réponse est un peu similaire à celle d'Ender2050, mais je préfère le faire via des tests de noms d'état:
$scope.isActive = function (stateName) {
var active = (stateName === $state.current.name);
return active;
};
hTML correspondant:
<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>
Voici une extension de la directive kfis que j'ai créée pour permettre différents niveaux de correspondance de chemin. J'ai essentiellement trouvé qu'il était nécessaire de faire correspondre les chemins d'URL jusqu'à une certaine profondeur, car la correspondance exacte ne permet pas les redirections d'état d'imbrication et par défaut. J'espère que cela t'aides.
.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");
}
});
}
};
}]);
Et voici comment j'utilise le lien
<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>
Cette directive correspondra au niveau de profondeur spécifié dans la valeur d'attribut pour la directive. Cela signifie simplement qu'il peut être utilisé ailleurs de nombreuses fois.
Si vous souhaitez que les liens de la directive soient placés dans un wrapper plutôt que de les sélectionner individuellement (cela facilite la consultation de la portée de Batarang), cela fonctionne également très bien:
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);
});
}
};
}
]);
Le balisage serait juste:
<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>
Je devrais également mentionner que j'utilise jQuery 'full-fat' dans cet exemple, mais vous pouvez facilement modifier ce que j'ai fait avec le filtrage et ainsi de suite.
Aucune des propositions ci-dessus directive ne m’a été utile. Si vous avez une barre de navigation bootstrap comme celle-ci
<ul class="nav navbar-nav">
<li><a ng-href="#/">Home</a></li>
<li><a ng-href="#/about">About</a></li>
...
</ul>
(cela pourrait être un démarrage de $ yo angular
) alors vous voulez ajouter .active
à la liste de classe d'élément parent <li>
, pas l'élément lui-même; i.e <li class="active">..</li>
. Alors j'ai écrit ceci:
.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);
}
})
}
}
}])
utilisation set-parent-active
; .active
est la valeur par défaut, il n'est donc pas nécessaire de le définir
<li><a ng-href="#/about" set-parent-active>About</a></li>
et l'élément <li>
parent sera .active
lorsque le lien sera actif. Pour utiliser une autre classe .active
telle que .highlight
, il suffit de
<li><a ng-href="#/about" set-parent-active="highlight">About</a></li>
Utilisation de Angular Version 6 avec Bootstrap 4.1
J'ai pu le faire comme on le voit ci-dessous.
Dans l'exemple ci-dessous, lorsque l'URL voit '/ contact', l'amorçage actif est ensuite ajouté à la balise html. Lorsque l'URL change, il est ensuite supprimé.
<ul>
<li class="nav-item" routerLink="/contact" routerLinkActive="active">
<a class="nav-link" href="/contact">Contact</a>
</li>
</ul>
Cette directive vous permet d'ajouter une classe CSS à un élément lorsque le lien est route devient active.
En savoir plus sur Site angulaire
J'ai trouvé la solution la plus simple. juste pour comparer indexOf en 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>
$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>
Voici mes deux cents, cela fonctionne très bien.
NOTE: Cela ne correspond pas à childpages (c'est ce dont j'avais besoin).
Vue:
<a ng-class="{active: isCurrentLocation('/my-path')}" href="/my-path" >
Some link
</a>
Manette:
// make sure you inject $location as a dependency
$scope.isCurrentLocation = function(path){
return path === $location.path()
}
Le plus important pour moi était de ne pas changer du tout le code de démarrage par défaut . Ici, c’est mon contrôleur de menu qui recherche les options de menu et ajoute ensuite le comportement souhaité.
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>
Je viens d'écrire une directive pour cela.
Usage:
<ul class="nav navbar-nav">
<li active><a href="#/link1">Link 1</a></li>
<li active><a href="#/link2">Link 2</a></li>
</ul>
La mise en oeuvre:
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();
});
});
});
eu le même problème. Voici ma solution :
.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'
])
Voici une bien meilleure façon de le faire
<ul>
<li ng-class="{ active: isActive('/tasks')}"><a href="/">Tasks</a></li>
</ul>
function tasksController($scope, $location)
{
$scope.isActive = function (viewLocation) {
return viewLocation === $location.path();
};
}
La route:
$routeProvider.when('/Account/', { templateUrl: '/Home/Account', controller: 'HomeController' });
Le menu html:
<li id="liInicio" ng-class="{'active':url=='account'}">
Le controlle:
angular.module('Home').controller('HomeController', function ($scope, $http, $location) {
$scope.url = $location.url().replace(/\//g, "").toLowerCase();
...
Le problème que j'ai trouvé ici est que l'élément de menu n'est actif que lorsque la page entière est chargée. Lorsque la vue partielle est chargée, le menu ne change pas. Quelqu'un sait pourquoi cela se produit?