8

I'm using a JSON service to list menu items (links). When navigating the different routes/pages I want an "active" class added to the link that is currently active (i.e. the page the user is on).

I've used this jsfiddle as a starting point: http://jsfiddle.net/p3ZMR/4/

I've also found several answers here on stackoverflow, but all of them are similar to the solution above.

But, that solution doesn't work if the links are generated via ng-repeat:

  <ul class="main-menu">
    <li ng-repeat="page in pages">
      <a href="/#/{{page.id}}" active-link="active">{{page.name}}</a>
    </li>
  </ul>

It seems as if the directive is called before the controller adds the links.

Is there any way to get around this?

3
  • possible duplicate of How to highlight a current menu item in AngularJS? Commented May 15, 2013 at 19:30
  • I don't think so. The solution there requires the links to be hard coded Commented May 15, 2013 at 19:39
  • Nope. You can just say getClass('/#/' + page.id) and it would not be hard coded. Commented May 15, 2013 at 19:47

4 Answers 4

5

HTML for repeating links

<div ng-app="link">
    <div data-ng-controller="myController">
        <a href="#/{{ link.url }}" data-ng-repeat="link in links" data-ng-class="(link.url == location.path() && 'active')">
            {{ link.name }}
        </a> 
    </div>
</div>

Javascript

angular.module('link', [])

function myController($scope, $location){
    $scope.location = $location;
     $scope.links = [
         { url: "one", name: "One"},
         { url: "two", name: "Two"},
         { url: "", name: "Three"}
    ];
}

This will produce links one/two/three with three being highlighted in the red color. Here's a jsfiddle: http://jsfiddle.net/4mFYy/1/

Sign up to request clarification or add additional context in comments.

4 Comments

Your jsfiddle doesn't seem to be working. The "active" class only gets added on click? Try changing .active color to "green" and it only works on pageload.
It doesn't work because none of the route provider stuff is wired up. It was just a demo to show you how to achieve it.
Ah, I think I missed a couple of details when I tried it, for example my url's are /#/foo so I need to check location.path() exluding the "/" i.e $location.path().replace("/", "");. Gonna give it another go.
If that's the case, i would just modify links to include the / in the url.
2

As the author of your starting point, I updated the fiddle, so it should work now for your dynamic urls :).

angular.module('link', []).
  directive('activeLink', ['$location', function(location) {
  return {
      restrict: 'A',
      link: function(scope, element, attrs, controller) {
          var clazz = attrs.activeLink;
          scope.location = location;
          scope.$watch('location.path()', function(newPath) {
              if (attrs.href.substring(1) === newPath) {  //<------ Here it is already interpolated
                element.addClass(clazz);
              } else {
                element.removeClass(clazz);
              }
          });
      }

  };

  }]).
  controller('MyCtrl', ['$scope', function(scope) {
    scope.pages=[
       { url: "one", name: "One"},
       { url: "two", name: "Two"},
       { url: "three", name: "Three"},
       { url: "", name: "home"}
    ];
  }]);

http://jsfiddle.net/p3ZMR/59/

The problem is, that the directive is reading out the href attribute, before it gets interpolated. I just changed it in a way, that the href will get read out, after it is interpolated.

Comments

1

A staple programming concept is separation of concerns. I personally do not like to keep logic or comparisons in my views. I would prefer to keep business logic and comparisons in the javascript

HTML

<ul class="main-menu">
    <li ng-repeat="page in pages">
        <a ng-click="menu(page)" ng-class="{ active: isActive(page.url)>{{page.name}}</a>
    </li>
</ul>

Javascript

angular.module('link', [])

function myController($scope, $location){ 
    $scope.links = [
     { url: "one", name: "One"},
     { url: "two", name: "Two"},
     { url: "", name: "Three"}
    ];

    $scope.isActive = function (viewLocation) {
        var pattern = '/' + viewLocation,
            re = new RegExp(pattern);
        return re.test($location.path());
    };

    $scope.menu = function (location) {
        $location.path('/' + location.location);
    };
}

Comments

0

Ng-class was made for this. Check out the comments on the angular docs page for ng-class as its not well documented.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.