จะไฮไลต์รายการเมนูปัจจุบันได้อย่างไร?


205

AngularJS ช่วยด้วยการตั้งค่าactiveคลาสบนลิงค์สำหรับหน้าปัจจุบันหรือไม่?

ฉันคิดว่ามันมีวิธีที่น่าอัศจรรย์ แต่ฉันก็หาไม่เจอ

เมนูของฉันดูเหมือนว่า:

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

และฉันมีตัวควบคุมสำหรับแต่ละของพวกเขาในเส้นทางของเราและTasksControllerActionsController

แต่ฉันไม่สามารถหาวิธีผูกคลาส "แอคทีฟ" บน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 ใด ๆ ที่ขึ้นต้นด้วย '/ task' (เช่น '/ task / 1 / reports')


4
นี่จะเป็นการจับคู่ทั้ง "/" และ "/ อะไรก็ได้" หรือหากคุณมีรายการเมนูหลายรายการที่มี URL คล้ายกันเช่น "/ test", "/ test / this", "/ test / this / path" หากคุณอยู่ / ทดสอบมันจะเน้นตัวเลือกเหล่านั้นทั้งหมด
เบ็นเลช

3
ฉันได้เปลี่ยนนี้ถ้า ($ location.path () == เส้นทาง) และเส้นทาง Y คือ "/ blah" ฯลฯ
ทิม

113
ฉันชอบสัญกรณ์ngClass="{active: isActive('/tasks')}ซึ่งisActive()จะส่งคืนบูลีนเนื่องจาก decouples คอนโทรลเลอร์และมาร์กอัป / สไตล์
Ed Hinchliffe

6
ในกรณีที่ทุกคนสงสัยเกี่ยวกับรหัสที่จะไม่เพิ่มขึ้นเป็นสองเท่าถ้าเส้นทางเป็น "/" นี่คือมัน (ขออภัยสำหรับการจัดรูปแบบ): $ scope.getClass = ฟังก์ชั่น (เส้นทาง) {ถ้า ($ location.path () substr (0, path.length) == path) {if (path == "/" && $ location.path () == "/") {return "active"; } ถ้า if (path == "/") {return ""; } ส่งคืน "active"} else {return ""}}

1
EdHinchliffe ชี้ให้เห็นแล้วว่าสิ่งนี้ผสมมาร์กอัปและตรรกะ นอกจากนี้ยังนำไปสู่การทำซ้ำเส้นทางและอาจทำให้เกิดข้อผิดพลาดในการคัดลอกและวาง ฉันพบว่าแนวทางสั่งการโดย @kfis แม้ว่าจะมีจำนวนบรรทัดมากขึ้นสามารถนำมาใช้ซ้ำได้มากขึ้นและช่วยให้มาร์กอัปสะอาดยิ่งขึ้น
A. Murray

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

ฉันไม่แน่ใจว่าคุณหมายถึงอะไรโดย "ระวัง hashbangs" ดูเหมือนว่ามันจะทำงานได้ตลอดเวลา คุณสามารถให้ตัวอย่างเคาน์เตอร์ได้หรือไม่?
Andriy Drozdyuk

7
ถ้าคุณกำลังพยายามที่จะใช้เงินทุนและจะต้องตั้งอยู่บนพื้นฐานของความยุ่งเหยิง href ของที่ภายใน li var path = $(element).children("a")[0].hash.substring(1);แล้วการใช้งาน สิ่งนี้จะใช้งานได้กับสไตล์ที่ต้องการ<li active-link="active"><a href="#/dashboard">Dashboard</a></li>
Dave

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;
William Neely

หากคุณใช้ 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;
};

ชุดข้อความนี้มีคำตอบอื่น ๆ ที่คล้ายกันเป็นจำนวนมาก

วิธีการตั้งค่าคลาสแอ็คทีฟ bootstrap navbar ด้วย Angular JS


1
ลบตัวแปรตามที่ไม่จำเป็น เพียงแค่ส่งคืนผลการเปรียบเทียบ return viewLocation === $location.path()
afarazit

33

เพียงเพื่อเพิ่มสองเซ็นต์ของฉันในการอภิปรายฉันได้ทำโมดูลเชิงมุมบริสุทธิ์ (ไม่มี jQuery) และจะทำงานกับ URL แฮชที่มีข้อมูล (เช่น#/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);
            }
        }
    }]);
}());

(แน่นอนคุณสามารถใช้ส่วน directive)

นอกจากนี้ยังมีมูลค่าการสังเกตเห็นว่าเรื่องนี้ไม่ทำงานสำหรับแฮชที่ว่างเปล่า (เช่นexample.com/#หรือเพียงแค่example.com) จะต้องมีอย่างน้อยหรือเพียงแค่example.com/#/ example.com#/แต่สิ่งนี้จะเกิดขึ้นโดยอัตโนมัติด้วย ngResource และสิ่งที่คล้ายกัน

และนี่คือซอ: http://jsfiddle.net/gy2an/8/


1
วิธีแก้ปัญหาที่ดีเยี่ยม แต่ไม่สามารถใช้งานได้ในการโหลดหน้าแรกเพียงแค่ระบุตำแหน่งเปลี่ยนขณะที่แอปใช้งานจริง ฉันอัปเดตข้อมูลโค้ดของคุณเพื่อจัดการกับสิ่งนั้น
Jerry

@ Jarek: ขอบคุณ! ได้ดำเนินการเปลี่ยนแปลงของคุณ ฉันไม่ได้มีปัญหากับสิ่งนี้เป็นการส่วนตัว แต่วิธีแก้ปัญหาของคุณดูเหมือนจะเป็นวิธีแก้ปัญหาที่ดีสำหรับผู้ที่ควรประสบปัญหานี้
Pylinux

2
ตอนนี้ฉันได้สร้าง repo Github สำหรับคำขอดึงหากใครมีความคิดที่ดี: github.com/Karl-Gustav/autoActive
Pylinux

ฉันเพิ่งแก้ไขข้อบกพร่องอีกเล็กน้อยที่จะเกิดขึ้นหากคุณใช้ ng-href .. พบได้ที่นี่: github.com/Karl-Gustav/autoActive/pull/3
Blake Niemyjski

มันจะดีถ้าสคริปต์นี้อนุญาตให้คุณระบุเส้นทางเพื่อให้คุณสามารถเปิดใช้งานองค์ประกอบอื่น ๆ ได้
Blake Niemyjski

22

ในกรณีของฉันฉันแก้ไขปัญหานี้ด้วยการสร้างคอนโทรลเลอร์แบบง่ายที่รับผิดชอบการนำทาง

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

และเพียงเพิ่ม ng-class เข้ากับองค์ประกอบดังนี้:

<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-router ยอดเยี่ยม
moljac024

เห็นด้วยฉันลืมที่จะพูดถึงตอนแรกว่ามันเป็นวิธีการแก้ปัญหา ui-router เท่านั้น
frankie4fingers

13

มีng-classคำสั่งซึ่งผูกตัวแปรและคลาส CSS นอกจากนี้ยังยอมรับวัตถุ (คู่ className กับค่าบูล)

นี่คือตัวอย่างhttp://plnkr.co/edit/SWZAqj


ขอบคุณ แต่วิธีนี้ใช้ไม่ได้กับเส้นทางเช่น: /test1/blahblahหรือไม่
Andriy Drozdyuk

ดังนั้นคุณจะบอกว่าactive: activePath=='/test1'ส่งกลับโดยอัตโนมัติ "ใช้งาน" เป็นเส้นทางเริ่มต้นด้วยสตริงที่กำหนด? นี่คือโอเปอเรเตอร์หรือ regex ที่กำหนดไว้ล่วงหน้าหรือไม่?
Andriy Drozdyuk

ขออภัยฉันไม่คิดว่าฉันเข้าใจความต้องการของคุณถูกต้อง นี่คือการคาดเดาใหม่ของฉันคุณต้องการทั้งไฮไลต์ลิงก์ 'test1' และลิงค์ 'test1 / blahblah' ที่ไฮไลต์เมื่อเส้นทางคือ 'test1 / blahblah' "ฉันถูกต้องหรือไม่ถ้านี่เป็นความต้องการคุณก็ถูกต้องแล้ว ทำงาน.
ติดสาย

3
นี่คือ plnkr ที่อัปเดตแล้ว: plnkr.co/edit/JI5DtK (ซึ่งตอบสนองความต้องการที่เดาได้) เพียงแสดงโซลูชันทางเลือก
Tosh

ฉันรู้ว่าคุณทำอะไรที่นั่น. แต่ฉันไม่ใช่แฟนของการ==ตรวจสอบซ้ำใน html
Andriy Drozdyuk

13

คำตอบจาก @ Renan-Tomal-Fernandes เป็นสิ่งที่ดี แต่จำเป็นต้องคู่ของการปรับปรุงการทำงานอย่างถูกต้อง เหมือนเดิมมันจะตรวจพบลิงก์ไปยังหน้าแรก (/) ตามที่เรียกแม้ว่าคุณจะอยู่ในส่วนอื่น

ดังนั้นฉันปรับปรุงมันเล็กน้อยนี่คือรหัส ผมทำงานกับเงินทุนเพื่อให้ส่วนที่ใช้งานอยู่ในองค์ประกอบแทนของ<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หากคุณสนใจว่าแท็บ bootstrap เวอร์ชัน Angular-UI นั้นเป็นอย่างไร)

ฉันชอบคำตอบของ kfis เกี่ยวกับการสร้างคำสั่งของคุณเองเพื่อจัดการกับสิ่งนี้ แต่ดูเหมือนว่ายุ่งยากที่จะมีคำสั่งที่จำเป็นต้องวางไว้ในทุกลิงก์ ดังนั้นฉันจึงได้สร้างคำสั่ง Angular ของฉันเองซึ่งวางไว้หนึ่งครั้งบนulดังนั้นผมจึงได้สร้างคำสั่งเชิงมุมของตัวเองซึ่งจะวางแทนในครั้งเดียวในกรณีที่คนอื่นพยายามทำสิ่งเดียวกันฉันคิดว่าฉันโพสต์ไว้ที่นี่แม้ว่าฉันจะพูดว่าวิธีแก้ปัญหาด้านบนหลายวิธีก็ใช้ได้เช่นกัน นี่เป็นโซลูชันที่ซับซ้อนขึ้นเล็กน้อยเมื่อไปถึงจาวาสคริปต์ แต่จะสร้างส่วนประกอบที่ใช้ซ้ำได้โดยมีมาร์กอัปน้อยที่สุด

นี่คือจาวาสคริปต์สำหรับคำสั่งและผู้ให้บริการเส้นทางสำหรับ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>

นี่คือ plunker สำหรับมัน: 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>

อืม ... นี่ดูเหมือนซับซ้อนกว่าคำตอบที่ยอมรับ คุณช่วยอธิบายข้อดีของสิ่งนี้ได้ไหม?
Andriy Drozdyuk

1
คุณจะต้องใช้ในสถานการณ์เมื่อเมนูของคุณอยู่นอก ng-view ตัวควบคุมมุมมองไม่สามารถเข้าถึงสิ่งที่อยู่ข้างนอกได้ดังนั้นฉันจึงใช้ $ rootScope เพื่อเปิดใช้งานการสื่อสาร หากเมนูของคุณอยู่ในมุมมอง ng ดังนั้นคุณจะไม่ได้รับประโยชน์จากการใช้โซลูชันนี้
mrt

4

ใช้ Angular เวอร์ชัน 6 พร้อม Bootstrap 4.1

ฉันสามารถทำมันได้อย่างที่เห็นด้านล่าง

ในตัวอย่างด้านล่างเมื่อ URL เห็น '/ contact' bootstrap ที่ใช้งานจะถูกเพิ่มไปยังแท็ก html เมื่อ URL เปลี่ยนแปลงจะถูกลบออก

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

คำสั่งนี้ให้คุณเพิ่มคลาส CSS ลงในองค์ประกอบเมื่อเส้นทางของลิงก์เปิดใช้งาน

อ่านเพิ่มเติมในเว็บไซต์เชิงมุม


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>) จะดีกว่า แต่ฉันไม่แน่ใจว่าจะเป็นไปได้หรือไม่
Andriy Drozdyuk

เพิ่งอัปเดตด้วยรุ่นที่ใหม่กว่า - แทนที่จะเป็นรายการที่ไม่มีการเรียงลำดับแบบคงที่ตอนนี้ฉันใช้เมนูแบบเลื่อนลงของ Boostrap เป็นรายการที่เลือก
Wesley Hales

นี่ดูเหมือนเป็นมุมเชิงสำนวนที่เหมือนกันมากที่สุด ดูเหมือนว่าจะสอดคล้องกับคำแนะนำที่ให้ไว้ที่stackoverflow.com/questions/14994391/…และหลีกเลี่ยงการทำซ้ำเส้นทางในมุมมองใน href และใน ng-class
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};
});

สิ่งนี้ช่วยให้คุณมีลิงค์ในส่วนที่มีคำสั่งติดตามแบบแอคทีฟ:

<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 โดยที่คลาสที่แอ็คทีฟควรถูกนำไปใช้กับพาเรนต์ li ไม่ใช่ลิงก์เอง
  • อนุญาตให้ทำให้ลิงก์ใช้งานได้หากเส้นทางที่ซ้อนอยู่ทำงานอยู่
  • อนุญาตให้ปิดการใช้งานลิงก์หากไม่ได้ใช้งานอยู่

รหัส:

.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จากนั้นลิงก์จะเปิดใช้งานหากตำแหน่งคือ/ url / 2 :

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

ตัวอย่าง Bootstrap parent li จะได้รับคลาสที่แอ็คทีฟ:

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

ทั้งหมด* ใช้งาน link-คุณลักษณะที่สามารถนำมาใช้ในการรวมกันใด ๆ ดังนั้นเงื่อนไขที่ซับซ้อนมากสามารถดำเนินการ


1

หากคุณต้องการลิงก์สำหรับคำสั่งใน wrapper แทนที่จะเลือกแต่ละลิงก์ (ทำให้ง่ายต่อการดูขอบเขตใน 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

ข้อเสนอแนะคำสั่งข้างต้นไม่มีประโยชน์สำหรับฉัน หากคุณมีแถบนำทางบูตเช่นนี้

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

ฉันลองใช้ขอบเขตแล้ว $ on ("$ routeChangeSuccess", ฟังก์ชั่น (เหตุการณ์, ปัจจุบัน, ก่อนหน้า) {ApplyActiveClass ();}); แต่จะใช้งานได้เฉพาะเมื่อมีการคลิกลิงก์และไม่ใช่ 'ในการโหลดหน้า' (คลิกที่ปุ่มรีเฟรช) เฝ้าดูสถานที่ได้ทำงานให้ฉัน
sawe

0

สิ่งที่สำคัญที่สุดสำหรับฉันคือไม่ต้องเปลี่ยนค่าเริ่มต้นของ bootstrap นี่คือตัวควบคุมเมนูของฉันที่ค้นหาตัวเลือกเมนูแล้วเพิ่มพฤติกรรมที่เราต้องการ

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>
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.