Làm thế nào để làm nổi bật một mục menu hiện tại?


205

AngularJS có giúp ích gì trong việc thiết lập một activelớp trên liên kết cho trang hiện tại không?

Tôi tưởng tượng có một số cách kỳ diệu được thực hiện, nhưng dường như tôi không thể tìm thấy.

Thực đơn của tôi trông như:

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

và tôi có bộ điều khiển cho từng người trong số họ trong các tuyến đường của mình: TasksControllerActionsController.

Nhưng tôi không thể tìm ra cách để liên kết lớp "hoạt động" trên các aliên kết đến các bộ điều khiển.

Có gợi ý nào không?

Câu trả lời:


265

vào xem

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

trên bộ điều khiển

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

Với điều này, liên kết nhiệm vụ sẽ có lớp hoạt động trong bất kỳ url nào bắt đầu bằng '/ nhiệm vụ' (ví dụ '/ nhiệm vụ / 1 / báo cáo')


4
Điều này sẽ kết thúc khớp cả "/" và "/ bất cứ thứ gì" hoặc nếu bạn có nhiều mục menu với các url tương tự, như "/ test", "/ test / this", "/ test / this / path" nếu bạn đang bật / kiểm tra, nó sẽ làm nổi bật tất cả các tùy chọn đó.
Ben Lesh

3
Tôi đã thay đổi điều này thành if ($ location.path () == path) và, đường dẫn y là "/ blah", v.v.
Tim

113
Tôi thích ký hiệu này ngClass="{active: isActive('/tasks')}, nơi isActive()sẽ trả về một boolean vì nó tách rời bộ điều khiển và đánh dấu / kiểu dáng.
Ed Hinchliffe

6
Chỉ trong trường hợp bất kỳ ai thắc mắc về mã không tăng gấp đôi nếu đường dẫn là "/", thì đây là (xin lỗi vì định dạng): $ scope.getClass = function (path) {if ($ location.path (). chất nền (0, path.length) == path) {if (path == "/" && $ location.path () == "/") {return "active"; } if if (path == "/") {return ""; } return "active"}

1
EdHinchliffe đã chỉ ra rằng điều này trộn lẫn đánh dấu và logic. Nó cũng dẫn đến việc sao chép đường dẫn và có thể dễ bị sao chép và dán lỗi. Tôi đã thấy rằng cách tiếp cận chỉ thị của @kfis, mặc dù nhiều dòng hơn, có thể tái sử dụng nhiều hơn và giữ cho đánh dấu sạch hơn.
A. Murray

86

Tôi đề nghị sử dụng một chỉ thị trên một liên kết.

Nhưng nó chưa hoàn hảo. Coi chừng các hashbang;)

Đây là javascript cho chỉ thị:

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

và đây là cách nó sẽ được sử dụng trong 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>

sau đó tạo kiểu với css:

.active { color: red; }

Tôi không chắc ý của bạn là gì khi "coi chừng hashbang". Có vẻ như nó sẽ luôn hoạt động. Bạn có thể cung cấp một ví dụ phản tác dụng?
Andriy Drozdyuk

7
Nếu bạn đang cố gắng sử dụng Bootstrap và cần thiết lập dựa trên hàm băm của a's href trong một li, thì hãy sử dụng var path = $(element).children("a")[0].hash.substring(1);. Điều này sẽ hoạt động cho một phong cách như<li active-link="active"><a href="#/dashboard">Dashboard</a></li>
Dave

2
Tôi sẽ thay đổi scope.$watch('location.path()', function(newPath) {cho scope.$on('$locationChangeStart', function(){.
sanfilippopablo 31/12/13

2
Nếu bạn đang sử dụng ng-href chỉ cần thay đổi: var path = attrs.href; thành var path = attrs.href||attrs.ngHref;
William Neely

Nếu bạn sử dụng Bootstrap và cần đưa lớp hoạt động lên <li>, bạn có thể đổi element.addClass(clazz);thànhelement.parent().addClass(clazz);
JamesRLamar

47

Đây là một cách tiếp cận đơn giản, hoạt động tốt với 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>

Trong bộ điều khiển AngularJS của bạn:

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

Chủ đề này có một số câu trả lời tương tự khác.

Làm cách nào để thiết lập lớp hoạt động của thanh điều khiển bootstrap với Angular JS?


1
Loại bỏ các biến vì nó là không cần thiết. Chỉ cần trả lại kết quả so sánh. return viewLocation === $location.path()
afarazit

33

Chỉ cần thêm hai xu của tôi vào cuộc tranh luận, tôi đã tạo ra một mô-đun góc thuần túy (không có jQuery) và nó cũng sẽ hoạt động với các hàm băm chứa dữ liệu. (ví dụ #/this/is/path?this=is&some=data)

Bạn chỉ cần thêm mô-đun như một phụ thuộc và auto-activevào một trong những tổ tiên của menu. Như thế này:

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

Và mô-đun trông như thế này:

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

(Tất nhiên bạn chỉ có thể sử dụng phần chỉ thị)

Cũng đáng lưu ý rằng điều này không hoạt động đối với băm trống (ví dụ example.com/#hoặc chỉ example.com) nó cần phải có ít nhất example.com/#/hoặc chỉ example.com#/. Nhưng điều này xảy ra tự động với ngResource và tương tự.

Và đây là câu đố: http://jsfiddle.net/gy2an/8/


1
Giải pháp tuyệt vời, tuy nhiên, nó không hoạt động khi tải trang ban đầu, chỉ trên locationChange trong khi ứng dụng đang hoạt động. Tôi đã cập nhật đoạn trích của bạn để xử lý việc đó.
Jerry

@Jarek: Cảm ơn! Đã thực hiện các thay đổi của bạn. Tôi đã không có vấn đề với cá nhân này, nhưng giải pháp của bạn có vẻ như là một giải pháp ổn định tốt cho những người nên gặp phải vấn đề này.
Pylinux

2
Bây giờ tôi đã tạo một repo Github cho các yêu cầu kéo nếu bất kỳ ai khác có ý tưởng hay: github.com/Karl-Gustav/autoActive
Pylinux

Tôi vừa sửa thêm một vài lỗi sẽ xảy ra nếu bạn đang sử dụng ng-href .. Điều này được tìm thấy ở đây: github.com/Karl-Gustav/autoActive/pull/3
Blake Niemyjski

Sẽ thật tuyệt nếu kịch bản này cho phép bạn chỉ định một đường dẫn để bạn có thể kích hoạt nó các yếu tố khác.
Blake Niemyjski

22

Trong trường hợp của tôi, tôi đã giải quyết vấn đề này bằng cách tạo một bộ điều khiển đơn giản chịu trách nhiệm điều hướng

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

Và bằng cách thêm ng-class vào thành phần như vậy:

<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

Đối với người dùng Bộ định tuyến AngularUI :

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

Và điều đó sẽ đặt một activelớp trên đối tượng được chọn.


2
Đây là một chỉ thị ui-router và nó không hoạt động nếu bạn sử dụng bộ định tuyến tích hợp, còn gọi là ngRoute. Điều đó nói rằng, ui-router là tuyệt vời.
moljac024

Đồng ý, tôi quên đề cập ban đầu rằng đó chỉ là giải pháp ui-router.
frankie4fingers

13

Có một lệnh ng-class, liên kết lớp biến và lớp css. Nó cũng chấp nhận đối tượng (cặp giá trị className và bool).

Dưới đây là ví dụ, http://plnkr.co/edit/SWZAqj


Cảm ơn, nhưng điều này sẽ không hoạt động với các đường dẫn như : /test1/blahblah, hoặc nó sẽ?
Andriy Drozdyuk

Vì vậy, bạn đang nói rằng active: activePath=='/test1'tự động trả về một "hoạt động" là đường dẫn bắt đầu với chuỗi đã cho? Đây có phải là một số loại toán tử được xác định trước hoặc regex?
Andriy Drozdyuk

Xin lỗi, tôi không nghĩ rằng tôi hiểu chính xác yêu cầu của bạn. Đây là dự đoán mới của tôi, bạn muốn cả liên kết 'test1' và liên kết 'test1 / blahblah' được tô sáng khi tuyến đường là 'test1 / blahblah'. "Tôi có đúng không? Nếu đây là yêu cầu, bạn có đúng không? làm việc.
Tosh

3
Dưới đây là bản cập nhật plnkr: plnkr.co/edit/JI5DtK (đáp ứng yêu cầu đoán) để chỉ hiển thị một giải pháp thay thế.
Tosh

Tôi thấy những gì bạn đã làm ở đó. Nhưng tôi không phải là một fan hâm mộ của ==kiểm tra lặp đi lặp lại trong html.
Andriy Drozdyuk

13

Câu trả lời từ @ Renan-tomal-fernandes là tốt, nhưng cần một vài cải tiến để hoạt động chính xác. Như vậy, nó luôn phát hiện liên kết đến trang chủ (/) là được kích hoạt, ngay cả khi bạn ở trong một phần khác.

Vì vậy, tôi đã cải thiện nó một chút, đây là mã. Tôi làm việc với Bootstrap vì vậy phần hoạt động nằm trong <li>phần tử thay vì phần <a>.

Bộ điều khiển

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

Bản mẫu

<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

Đây là giải pháp mà tôi đã đưa ra sau khi đọc một số gợi ý tuyệt vời ở trên. Trong tình huống cụ thể của tôi, tôi đã cố gắng sử dụng thành phần tab Bootstrap làm menu của mình, nhưng tôi không muốn sử dụng phiên bản Angular-UI của điều này vì tôi muốn các tab hoạt động như một menu, trong đó mỗi tab có thể đánh dấu trang, thay vì các tab đóng vai trò điều hướng cho một trang. (Xem http://angular-ui.github.io/bootstrap/#/tabs nếu bạn quan tâm đến phiên bản Angular-UI của các tab bootstrap trông như thế nào).

Tôi thực sự thích câu trả lời của kfis về việc tạo ra chỉ thị của riêng bạn để xử lý vấn đề này, tuy nhiên có vẻ rườm rà khi có một chỉ thị cần được đặt trên mỗi liên kết. Vì vậy, tôi đã tạo ra chỉ thị Angular của riêng mình được đặt thay thế một lần trên ul. Chỉ trong trường hợp bất kỳ ai khác đang cố gắng làm điều tương tự, tôi nghĩ rằng tôi đã đăng nó ở đây, mặc dù như tôi đã nói, nhiều giải pháp trên cũng hoạt động tốt. Đây là một giải pháp phức tạp hơn một chút theo như javascript, nhưng nó tạo ra một thành phần có thể sử dụng lại với đánh dấu tối thiểu.

Đây là javascript cho chỉ thị và nhà cung cấp tuyến đường cho 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");
            });
        }

    };

 }]);

Sau đó, trong html của bạn, bạn chỉ cần:

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

Đây là trình tải cho nó: http://plnkr.co/edit/xwGtGqrT7kWoCKnGDHYN?p=preview .


9

Bạn có thể thực hiện điều này rất đơn giản, đây là một ví dụ:

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

Và bộ điều khiển của bạn nên là cái này:

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


4

Tôi gặp vấn đề tương tự với menu nằm ngoài phạm vi điều khiển. Không chắc đây là giải pháp tốt nhất hay là một giải pháp được đề xuất nhưng đây là những gì làm việc cho tôi. Tôi đã thêm phần sau vào cấu hình ứng dụng của mình:

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

Sau đó, trong quan điểm tôi có:

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

Ừm ... điều này có vẻ phức tạp hơn mà câu trả lời được chấp nhận. Bạn có thể mô tả lợi thế của cái này hơn cái kia không?
Andriy Drozdyuk

1
Bạn sẽ cần nó trong kịch bản khi menu của bạn nằm ngoài ng-view. Xem bộ điều khiển sẽ không có quyền truy cập vào bất cứ thứ gì bên ngoài vì vậy tôi đã sử dụng $ rootScope để kích hoạt liên lạc. Nếu menu của bạn nằm trong chế độ xem ng thì sẽ không có lợi cho bạn khi sử dụng giải pháp này.
mrt

4

Sử dụng Angular Phiên bản 6 với Bootstrap 4.1

Tôi đã có thể làm cho nó được thực hiện như được thấy dưới đây.

Trong ví dụ bên dưới, khi URL thấy '/ contact', thì bootstrap kích hoạt sau đó được thêm vào thẻ html. Khi URL thay đổi, nó sẽ bị xóa.

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

Lệnh này cho phép bạn thêm một lớp CSS vào một phần tử khi tuyến của liên kết bắt đầu hoạt động.

Đọc thêm trên trang web Angular


3

Sử dụng một lệnh (vì chúng ta đang thực hiện thao tác DOM ở đây), đây có lẽ là cách gần nhất để thực hiện mọi thứ theo "cách góc":

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

Sau đó, HTML của bạn sẽ trông như sau:

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

Hấp dẫn. Nhưng menu-itemdường như dư thừa trên mỗi dòng. Có lẽ việc gắn một thuộc tính vào thành ulphần (ví dụ <ul menu>) sẽ tốt hơn, nhưng tôi không chắc liệu điều đó có khả thi hay không.
Andriy Drozdyuk

Chỉ được cập nhật với phiên bản mới hơn - thay vì danh sách không có thứ tự tĩnh, hiện tôi đang sử dụng menu thả xuống Boostrap làm danh sách chọn.
Wesley Hales

Điều này có vẻ như góc cạnh thành ngữ nhất. Nó có vẻ phù hợp với lời khuyên được đưa ra tại stackoverflow.com/questions/14994391/ , và nó tránh trùng lặp đường dẫn trong chế độ xem, trong href và trong lớp ng.
gây quỹ

2

Tôi đã làm nó như thế này:

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

Điều này cho phép bạn có các liên kết trong một phần có chỉ thị theo dõi hoạt động:

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

Cách tiếp cận này có vẻ sạch sẽ hơn nhiều so với những người khác, với tôi.

Ngoài ra, nếu bạn đang sử dụng jQuery, bạn có thể làm cho nó gọn gàng hơn rất nhiều vì jQlite chỉ có hỗ trợ bộ chọn cơ bản. Một phiên bản sạch hơn nhiều với jquery bao gồm trước góc cạnh bao gồm sẽ như thế này:

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

Đây là một jsFiddle


2

Giải pháp của tôi cho vấn đề này, sử dụng route.currenttrong các mẫu góc.

Khi bạn có /taskstuyến đường để tô sáng trong menu, bạn có thể thêm thuộc tính của riêng mình menuItemvào các tuyến đường được mô-đun khai báo:

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

Sau đó, trong mẫu của tasks.htmlbạn, bạn có thể sử dụng ng-classchỉ thị sau :

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

Theo tôi, điều này sạch hơn nhiều so với tất cả các giải pháp được đề xuất.


1

Đây là một phần mở rộng trên chỉ thị kfis mà tôi đã làm để cho phép các mức độ khớp đường dẫn khác nhau. Về cơ bản, tôi thấy nhu cầu đối với các đường dẫn URL phù hợp với độ sâu nhất định, vì khớp chính xác không cho phép chuyển hướng trạng thái lồng nhau và mặc định. Hi vọng điêu nay co ich.

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

        };
    }]);

Và đây là cách tôi sử dụng liên kết

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

Lệnh này sẽ khớp với mức độ sâu được chỉ định trong giá trị thuộc tính cho lệnh. Chỉ có nghĩa là nó có thể được sử dụng ở nơi khác nhiều lần hơn.


1

Đây là một chỉ thị khác để làm nổi bật các liên kết hoạt động.

Các tính năng chính:

  • Hoạt động tốt với href có chứa các biểu thức góc động
  • Tương thích với điều hướng băm
  • Tương thích với Bootstrap nơi lớp hoạt động nên được áp dụng cho cha mẹ chứ không phải chính liên kết
  • Cho phép tạo liên kết hoạt động nếu bất kỳ đường dẫn lồng nhau nào đang hoạt động
  • Cho phép vô hiệu hóa liên kết nếu nó không hoạt động

Mã số:

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

Sử dụng:

Ví dụ đơn giản với biểu thức góc, giả sử $ scope.var = 2 , sau đó liên kết sẽ hoạt động nếu vị trí là / url / 2 :

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

Bootstrap ví dụ, cha mẹ li sẽ nhận được lớp hoạt động:

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

Ví dụ với các url lồng nhau, liên kết sẽ hoạt động nếu bất kỳ url lồng nhau nào đang hoạt động (tức là / url / 1 , / url / 2 , url / 1/2 / ... )

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

Ví dụ phức tạp, liên kết trỏ đến một url ( / url1 ) nhưng sẽ hoạt động nếu chọn một url khác ( / url2 ):

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

Ví dụ với liên kết bị vô hiệu hóa, nếu nó không hoạt động, nó sẽ có lớp 'bị vô hiệu hóa :

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

Tất cả các thuộc tính active-link- * có thể được sử dụng trong mọi kết hợp, do đó các điều kiện rất phức tạp có thể được thực hiện.


1

Nếu bạn muốn các liên kết cho lệnh trong một trình bao bọc thay vì chọn từng liên kết riêng lẻ (giúp dễ dàng xem xét phạm vi trong Batarang), thì điều này cũng hoạt động khá tốt:

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

Đánh dấu sẽ chỉ là:

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

Tôi cũng nên đề cập rằng tôi đang sử dụng jQuery 'full-fat' trong ví dụ này, nhưng bạn có thể dễ dàng thay đổi những gì tôi đã làm với bộ lọc, v.v.


1

Đây là hai xu của tôi, nó hoạt động tốt.

LƯU Ý: Điều này không phù hợp với các trang con (đó là những gì tôi cần).

Lượt xem:

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

Điều khiển:

// make sure you inject $location as a dependency

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

1

Theo câu trả lời của @kfis, đó là ý kiến ​​và khuyến nghị của tôi, chỉ thị cuối cùng như dưới đây:

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

và đây là cách nó sẽ được sử dụng trong 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>

sau đó tạo kiểu với css:

.active { color: red; }

1

Đối với những người sử dụng bộ định tuyến ui, câu trả lời của tôi có phần giống với Ender2050, nhưng tôi thích làm điều này thông qua kiểm tra tên trạng thái:

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

HTML tương ứng:

<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

Không có gợi ý chỉ thị nào ở trên hữu ích với tôi. Nếu bạn có một thanh điều hướng bootstrap như thế này

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

(đó có thể là một $ yo angularkhởi động) sau đó bạn muốn thêm .activevào danh sách lớp phần tử cha <li> , chứ không phải chính phần tử đó; tức <li class="active">..</li>. Vì vậy, tôi đã viết này:

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

sử dụng set-parent-active; .activelà mặc định nên không cần thiết phải đặt

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

<li>phần tử cha sẽ là .activekhi liên kết được kích hoạt. Để sử dụng một .activelớp thay thế như .highlight, chỉ cần

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

Tôi đã thử phạm vi. $ Trên ("$ routeChangeSuccess", hàm (sự kiện, hiện tại, trước đó) {applicationActiveClass ();}); nhưng nó chỉ hoạt động khi liên kết được nhấp và không 'tải trang' (nhấp vào nút refresh). xem vị trí đã làm việc cho tôi
cưa

0

Quan trọng nhất đối với tôi là không thay đổi tất cả mã mặc định bootstrap. Đây là trình điều khiển menu của tôi để tìm kiếm các tùy chọn menu và sau đó thêm hành vi chúng ta muốn.

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

có vấn đề tương tự Đây là giải pháp của tôi :

.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

Tôi chỉ viết một chỉ thị cho việc này.

Sử dụng:

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

Thực hiện:

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

Các xét nghiệm:

'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

Tuyến đường:

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

Menu html:

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

Bộ điều khiển:

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

Vấn đề tôi tìm thấy ở đây là mục menu chỉ hoạt động khi toàn bộ trang được tải. Khi chế độ xem một phần được tải, menu sẽ không thay đổi. Ai đó biết tại sao nó xảy ra?


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

Tôi tìm thấy giải pháp dễ nhất. chỉ để so sánh indexOf trong 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>
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.