Как выделить текущий пункт меню?


205

Помогает ли AngularJS каким-либо образом active класс для ссылки на текущую страницу?

Я предполагаю, что есть какой-то волшебный способ сделать это, но я не могу найти.

Мое меню выглядит так:

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

и у меня есть контроллеры для каждого из них в моих маршрутах: TasksControllerи ActionsController.

Но я не могу придумать, как связать «активный» класс с aссылками на контроллеры.

Есть намеки?

Ответы:


265

на вид

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

на контроллере

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

При этом ссылка задач будет иметь активный класс в любом URL-адресе, начинающемся с «/ tasks» (например, «/ tasks / 1 / reports»)


4
Это в конечном итоге приведет к совпадению как «/», так и «/ что-нибудь», или если у вас есть несколько пунктов меню с одинаковыми URL, такими как «/ test», «/ test / this», «/ test / this / path», если вы были на / test, он бы выделил все эти опции.
Бен Леш

3
Я изменил это на if ($ location.path () == path) и, y path это "/ blah" и т. Д.
Tim

113
Я предпочитаю нотацию ngClass="{active: isActive('/tasks')}, где isActive()будет возвращаться логическое значение, поскольку оно разъединяет контроллер и разметку / стиль.
Эд Хинчлифф,

6
На всякий случай, если кому-то интересно, чтобы код не удваивался, если путь «/», то это так (извините за форматирование): $ 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 уже указал, что это смешивает разметку и логику. Это также приводит к дублированию пути и может привести к ошибкам копирования и вставки. Я обнаружил, что директивный подход @kfis, хотя и содержит больше строк, более пригоден для повторного использования и сохраняет чистоту разметки.
А. Мюррей

86

Я предлагаю использовать директиву по ссылке.

Но это еще не идеально. Остерегайтесь hashbangs;)

Вот javascript для директивы:

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

и вот как это будет использоваться в 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>

потом стиль с помощью CSS:

.active { color: red; }

Я не уверен, что вы имеете в виду под "остерегаться хэшбанков". Кажется, это всегда будет работать. Не могли бы вы привести контрпример?
Андрей Дроздюк

7
Если вы пытаетесь использовать Bootstrap и вам нужно установить его, основываясь на хэше a href внутри li, используйте var path = $(element).children("a")[0].hash.substring(1);. Это будет работать для стиля, как<li active-link="active"><a href="#/dashboard">Dashboard</a></li>
Дейв

2
Я бы поменял scope.$watch('location.path()', function(newPath) {наscope.$on('$locationChangeStart', function(){ .
sanfilippopablo

2
Если вы используете ng-href, просто измените: var path = attrs.href; на var path = attrs.href||attrs.ngHref;
Уильям Нили

Если вы используете Bootstrap и вам нужно поставить активный класс на <li>, вы можете изменить его element.addClass(clazz);наelement.parent().addClass(clazz);
JamesRLamar

47

Вот простой подход, который хорошо работает с 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>

В вашем контроллере AngularJS:

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

Эта тема имеет ряд других похожих ответов.

Как установить загрузочный класс Navbar с Angular JS?


1
Удалите переменную, так как она не нужна. Просто верните результат сравнения. return viewLocation === $location.path()
афаразит

33

Просто чтобы добавить два цента в дебаты, я сделал чисто угловой модуль (без jQuery), и он также будет работать с хеш-адресами, содержащими данные. (например#/this/is/path?this=is&some=data )

Вы просто добавляете модуль в качестве зависимости и auto-activeк одному из предков меню. Как это:

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

И модуль выглядит так:

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

(Конечно, вы можете просто использовать директивную часть)

Также стоит заметить, что это не работает для пустых хешей (например, example.com/#или просто example.com), которые должны быть хотя бы example.com/#/или просто example.com#/. Но это происходит автоматически с ngResource и т.п.

А вот и скрипка: http://jsfiddle.net/gy2an/8/


1
Отличное решение, однако оно не сработало при начальной загрузке страницы, только на locationChange, пока приложение работает. Я обновил ваш фрагмент, чтобы справиться с этим.
Джерри

@Jarek: Спасибо! Внедрили ваши изменения. У меня лично не было проблем с этим, но ваше решение кажется хорошим стабильным решением для тех, кто должен столкнуться с этой проблемой.
Pylinux

2
Теперь я создал репозиторий Github для запросов извлечения, если у кого-то еще есть хорошие идеи: github.com/Karl-Gustav/autoActive
Pylinux

Я просто исправил еще несколько ошибок, которые возникали бы, если бы вы использовали ng-href .. Это можно найти здесь: github.com/Karl-Gustav/autoActive/pull/3
Блейк Нимейски,

Было бы хорошо, если бы этот скрипт позволял вам указывать путь, чтобы вы могли активировать другие элементы.
Блейк Немийский

22

В моем случае я решил эту проблему, создав простой контроллер, отвечающий за навигацию

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

И просто добавив ng-класс к элементу, вот так:

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

14

Для пользователей AngularUI Router:

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

И это поместит activeкласс на выбранный объект.


2
Это директива ui-router, и она не работает, если вы используете встроенный маршрутизатор, иначе ngRoute. Тем не менее, UI-роутер это круто.
moljac024

Согласитесь, я забыл упомянуть, что это было решение только для ui-роутера.
frankie4fingers

13

Eсть ng-class директива, которая связывает переменную и класс css. Он также принимает объект (className против пар значения bool).

Вот пример, http://plnkr.co/edit/SWZAqj


Спасибо, но это не сработает с путями типа: /test1/blahblahили не так ли?
Андрей Дроздюк

Так вы говорите, что active: activePath=='/test1'автоматически возвращает «активный» путь начинается с заданной строки? Это какой-то предопределенный оператор или регулярное выражение?
Андрей Дроздюк

Извините, я не думаю, что правильно понял ваше требование. Вот мое новое предположение, вы хотите, чтобы выделенная ссылка 'test1' И ссылка 'test1 / blahblah' были выделены, когда для маршрута указано 'test1 / blahblah'. "Я прав? Если это требование, вы правы, что мое решение не дает работа
Тош

3
Вот обновленный plnkr: plnkr.co/edit/JI5DtK (который удовлетворяет предполагаемому требованию), чтобы просто показать альтернативное решение.
Тош

Я вижу что ты тут делал. Но я не фанат повторных ==проверок в html.
Андрей Дроздюк

13

ответ от @ Ренан-tomal-Фернандеса это хорошо, но нужно несколько улучшений работы правильно. Как бы то ни было, он всегда будет обнаруживать ссылку на домашнюю страницу (/) как активированную, даже если вы находитесь в другом разделе.

Так что я немного его улучшил, вот код. Я работаю с Bootstrap, поэтому активная часть находится в <li>элементе вместо <a>.

контроллер

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

шаблон

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

10

Вот решение, которое я придумала после прочтения некоторых из превосходных предложений выше. В моей конкретной ситуации я пытался использовать компонент вкладок Bootstrap в качестве моего меню, но не хотел использовать его версию Angular-UI, потому что я хочу, чтобы вкладки действовали как меню, где каждая вкладка имеет возможность закладки, а не вкладки, выступающие в качестве навигации для одной страницы. (См. Http://angular-ui.github.io/bootstrap/#/tabs, если вам интересно, как выглядит версия загрузочных вкладок Angular-UI).

Мне очень понравился ответ kfis о создании вашей собственной директивы, чтобы справиться с этим, однако было неудобно иметь директиву, которую нужно было разместить в каждой отдельной ссылке. Поэтому я создал свою собственную директиву Angular, которая вместо этого помещается однажды в ul. На случай, если кто-то еще попытается сделать то же самое, я решил опубликовать это здесь, хотя, как я уже сказал, многие из вышеперечисленных решений также работают. Это немного более сложное решение с точки зрения javascript, но оно создает повторно используемый компонент с минимальной разметкой.

Вот javascript для директивы и провайдера маршрутов для 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");
            });
        }

    };

 }]);

Тогда в вашем html вы просто:

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

Вот плункер для этого: http://plnkr.co/edit/xwGtGqrT7kWoCKnGDHYN?p=preview .


9

Вы можете реализовать это очень просто, вот пример:

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

И ваш контроллер должен быть таким:

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


4

У меня была похожая проблема с меню, расположенным вне области контроллера. Не уверен, что это лучшее решение или рекомендуемое, но это то, что сработало для меня. Я добавил следующее в конфигурацию своего приложения:

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

Тогда по моему мнению:

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

Хм ... это кажется более сложным, чем принятый ответ. Не могли бы вы описать преимущество этого по сравнению с этим?
Андрей Дроздюк

1
Он понадобится вам в сценарии, когда ваше меню находится за пределами ng-view. Контроллер представления не будет иметь доступа ко всему, что находится снаружи, поэтому я использовал $ rootScope для обеспечения связи. Если ваше меню находится внутри ng-view, использование этого решения не принесет вам никакой пользы.
MRT

4

Использование Angular версии 6 с Bootstrap 4.1

Я смог сделать это, как показано ниже.

В приведенном ниже примере, когда в URL-адресе отображается «/ contact», активная начальная загрузка добавляется в тег html. Когда URL-адрес изменяется, он удаляется.

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

Эта директива позволяет вам добавить класс CSS к элементу, когда маршрут ссылки становится активным.

Узнайте больше на сайте Angular


3

Используя директиву (поскольку мы здесь выполняем манипуляции с DOM), следующее, вероятно, ближе всего подходит к «угловому пути»:

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

Тогда ваш HTML будет выглядеть так:

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

Интересный. Но menu-itemкажется избыточным в каждой строке. Возможно, лучше было бы прикрепить атрибут к ulэлементу (например <ul menu>), но я не уверен, возможно ли это.
Андрей Дроздюк

Только что обновлен новой версией - вместо статического неупорядоченного списка, я теперь использую выпадающее меню Boostrap в качестве списка выбора.
Уэсли Хейлз

Это похоже на идиоматическое угловатое. Кажется, это соответствует совету, данному на stackoverflow.com/questions/14994391/… , и позволяет избежать дублирования пути в представлении, в href и в ng-классе.
fundead

2

Я сделал это так:

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

Это позволяет вам иметь ссылки в разделе, который имеет директиву track-active:

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

Этот подход кажется мне намного чище, чем другие.

Кроме того, если вы используете jQuery, вы можете сделать его намного лучше, потому что jQlite имеет только базовую поддержку селектора. Гораздо более чистая версия с включенным jquery перед угловым включением будет выглядеть так:

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

Вот jsFiddle


2

Мое решение этой проблемы используйте route.currentв угловом шаблоне.

Поскольку у вас есть /tasksмаршрут для выделения в вашем меню, вы можете добавить свое собственное свойство menuItemк маршрутам, объявленным вашим модулем:

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

Тогда в вашем шаблоне tasks.htmlвы можете использовать следующую ng-classдирективу:

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

На мой взгляд, это намного чище, чем все предлагаемые решения.


1

Вот расширение директивы kfis, которое я сделал, чтобы учесть разные уровни соответствия пути. По сути, я обнаружил необходимость сопоставления URL-путей до определенной глубины, поскольку точное сопоставление не допускает вложений и перенаправлений состояний по умолчанию. Надеюсь это поможет.

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

        };
    }]);

А вот как я использую ссылку

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

Эта директива будет соответствовать уровню глубины, указанному в значении атрибута для директивы. Просто означает, что это может быть использовано в другом месте много раз.


1

Вот еще одна директива для выделения активных ссылок.

Ключевая особенность:

  • Хорошо работает с href, который содержит динамические угловые выражения
  • Совместим с хэш-бэнг навигацией
  • Совместим с Bootstrap, где активный класс должен применяться к родителю, а не к самой ссылке
  • Позволяет сделать ссылку активной, если активен какой-либо вложенный путь
  • Позволяет сделать ссылку отключенной, если она не активна

Код:

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

Использование:

Простой пример с угловым выражением, скажем, $ scope.var = 2 , тогда ссылка будет активной, если location это / url / 2 :

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

Пример начальной загрузки, родительский ли получит активный класс:

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

Пример с вложенными URL, ссылка будет активной, если активен любой вложенный URL (т.е. / url / 1 , / url / 2 , url / 1/2 / ... )

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

Сложный пример, ссылка указывает на один URL ( / url1 ), но будет активна, если выбран другой ( / url2 ):

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

Пример с отключенной ссылкой, если она не активна, у нее будет класс «отключен» :

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

Все атрибуты active-link- * могут использоваться в любой комбинации, поэтому могут быть реализованы очень сложные условия.


1

Если вам нужны ссылки для директивы в оболочке, а не для выбора каждой отдельной ссылки (это упрощает просмотр области действия в Batarang), это тоже работает довольно хорошо:

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

Разметка будет просто:

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

Я должен также упомянуть, что в этом примере я использую jQuery «полный жир», но вы можете легко изменить то, что я сделал с фильтрацией и так далее.


1

Вот мои два цента, это прекрасно работает.

ПРИМЕЧАНИЕ: это не соответствует дочерним страницам (что мне и нужно).

Посмотреть:

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

контроллер:

// make sure you inject $location as a dependency

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

1

Согласно ответу @kfis, это комментарии, и я рекомендую окончательную директиву, как показано ниже:

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

и вот как это будет использоваться в 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>

потом стиль с помощью CSS:

.active { color: red; }

1

Для тех, кто использует ui-router, мой ответ несколько похож на Ender2050, но я предпочитаю делать это с помощью проверки имени состояния:

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

соответствующий 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>

1

Ни один из перечисленных выше директив предложений не были полезны для меня. Если у вас есть загрузчик Navbar, как это

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

(это может быть $ yo angularзапуск), затем вы хотите добавить .activeв список классов родительского <li> элемента, а не сам элемент; то есть <li class="active">..</li>. Итак, я написал это:

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

использование set-parent-active; .activeпо умолчанию, поэтому не нужно устанавливать

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

и родительский <li>элемент будет, .activeкогда ссылка активна. Чтобы использовать альтернативный .activeкласс, как .highlight, просто

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

Я пробовал scope. $ On ("$ routeChangeSuccess", function (event, current, previous) {applyActiveClass ();}); но это работает только тогда, когда ссылка нажата, а не «при загрузке страницы» (нажатие на кнопку обновления). просмотр местоположения сработал для меня
sawe

0

Самым важным для меня было вообще не менять код начальной загрузки. Вот мой контроллер меню, который ищет пункты меню и затем добавляет желаемое поведение.

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

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

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

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

0

была такая же проблема. Вот мое решение :

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

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

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

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

0

Я просто написал директиву для этого.

Использование:

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

Реализация:

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

тесты:

'use strict';

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

0

Маршрут:

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

Меню HTML:

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

Контроллер:

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

Проблема, которую я обнаружил здесь, заключается в том, что пункт меню активен только при полной загрузке страницы. При частичном просмотре меню не меняется. Кто-нибудь знает, почему это происходит?


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


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

0

Я нашел самое простое решение. просто сравнить indexOf в 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>
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.