Question

I'm working on a project that has implemented the UI router and it's using ui-sref-active="active" to add the active class to the navigation menu item when that item is the current route. However, when you navigate to a nested view within that view, the parent menu item is no longer active. See the following Plunker:

http://plnkr.co/edit/2CoEdS?p=preview

By default (or if you click on it) Route 1 is "active". When you click on "Show List," you will see that Route 1 is no longer active.

Edit:

The only difference between this example and my actual project is that the navigation menu in my actual project has its own controller and so does not use the same scope as the controller for "route1".

Was it helpful?

Solution

EDIT For updated ui-router 0.2.13:

ui-sref-active="active" now sets the 'active' class when the current state is the ui-sref's state or any child

ui-sref-active-eq="active" behaves as the previous iterations of ui-sref-active, and only sets the class for the exact state

Original Answer:

See open ui-router issues: https://github.com/angular-ui/ui-router/issues/704 and 818

A general workaround people are suggesting is:

ng-class="{active:$state.includes('route1')}"

Of course, $state must be added to $scope. See updated plunk: http://plnkr.co/edit/KHLDJP?p=preview

OTHER TIPS

You are having a wrong understanding of ui-sref-active="active"

<li ui-sref-active="active"><a ui-sref="route1">Route 1</a></li>

This will show special css highlighting only when you are in state route1 (reference https://github.com/angular-ui/ui-router/wiki/Quick-Reference#wiki-ui-sref-active). This is the case when you click on route 1. But when you click on "Show list" you are no longer in route1. Rather you are in state "route1.list" . You can verify this by writing the following code. This is strictly for understanding how state works.

js

inside controller

$rootScope.currentState = $state.$current.name //using rootScope so that you can access the variable anywhere inside html

inside html

{{currentState}}

If you look closely at documentation of ui-sref-active, it not only looks at stateName but also stateParams, hence when you go to substate it no longer changes css. From the sourcecode it becomes clearer.

 function update() {
        if ($state.$current.self === state && matchesParams()) {
          $element.addClass(activeClass);
        } else {
          $element.removeClass(activeClass);
        }// route1===route1.list will not be true.

to solve the problem, remember scope variables are inherited in nested views.

inside controller of route.

$scope.route1Active = true;

in html

<li ng-class={active:route1Active}><a ui-sref="route1">Route 1</a></li>

Angular UI router now supports this natively. See commit https://github.com/angular-ui/ui-router/commit/bf163ad6ce176ce28792696c8302d7cdf5c05a01

My solution was to set:

<li ng-class="{ 'active': state.current.name.indexOf('route1') != -1 }">

The state was already previously added to the controller's scope:

$scope.state = $state;

You do not need to do any thing in the controllers. Here is my example code:

    <ul class="nav nav-pills nav-stacked" role="tablist">
        <li ui-sref-active="active"><a ui-sref="Booking.Step1" href="#/Booking/Step1">Step1</a></li>
        <li ui-sref-active="active"><a ui-sref="Booking.Step2" href="#/Booking/Step2" >Step2</a></li>
        <li ui-sref-active="active"><a ui-sref="Booking.Step3" href="#/Booking/Step3">Step3</a></li>
        <li ui-sref-active="active"><a ui-sref="Booking.Step4" href="#/Booking/Step4">Step4</a></li>
        <li ui-sref-active="active"><a ui-sref="Booking.Step5" href="#/Booking/Step5">Step5</a></li>
    </ul>

In route configuration:

$stateProvider.state('Booking', {
        abstract: true,
        url: '/Booking',
        templateUrl: "TourApp/Templates/Booking/SideMenu.html",
        controller: "SideMenuController"
    });

    $stateProvider.state('Booking.Step1', {
        url: "/Step1",
        views: {
            'content': {
                templateUrl: "TourApp/Templates/Booking/Step1.html",
                controller: "Step1Controller"
            }
        }
    });

    $stateProvider.state('Booking.Step2', {
        url: "/Step2",
        views: {
            'content': {
                templateUrl: "TourApp/Templates/Booking/Step2.html",
                controller: "Step2Controller"
            }
        }
    });

Now they have updated and the new way to do that is

 <a ui-sref-active="{'active': 'main.app.manage.**'}" ui-sref="main.app.manage.people.list"></a>

Below is the state file

 angular.module('manage.people', ['people.invite'])
  .config(['$stateProvider', function($stateProvider) {
    $stateProvider
      .state('main.app.manage.people', {
        url: '/people',
        abstract: true,
        cache: false,
        views: {
          'tabContent': {
            template: '<div ui-view="peopleContent"></div>',
            controller: 'peopleController'
          }
        }
      })
      .state('main.app.manage.people.list', {
        url: '/list',
        views: {
          'peopleContent': {
            templateUrl: '/internal/main/app/manage/people/views/people.html',
            controller: 'peopleListController'
          }
        }
      });

We already have a solution without any "hack" HERE

That's the way to do:

HTML >

 <li ui-sref-active="active" >
   <a href="#" class="submenu" ui-sref="bands">
       <i class="fa fa-location-arrow" aria-hidden="true"></i>
                     Bands
           <span class="fa fa-chevron-down"></span>
   </a>

   <ul class="nav child_menu">
        <li ui-sref-active="active">
            <a  ui-sref="bands.nirvana">
                 Nirvana   
            </a>
       </li>
       <li ui-sref-active="active">
            <a  ui-sref="bands.iron">
                Iron
            </a>
       </li>
       <li ui-sref-active="active">
            <a  ui-sref="bands.metalica">
                Metalica
            </a>
       </li>           
  </ul>

Our router config will be like this >

$stateProvider.state('bands', {
    abstract: true,
    url: '/bands',
    templateUrl: "myapp/categories/template.bands.html", //<ui-view></ui-view> 
    controller: "SomeController as vm"
}).state('bands.nirvana', {
    url: '/nirvana',
    templateUrl: "myapp/categories/band.nirvana.html",
    controller: "SomeController as vm"
}).state('bands.iron', {
    url: '/iron',
    templateUrl: "myapp/categories/band.iron.html",
    controller: "SomeController as vm"
}).state('bands.meatlica', {
    url: '/metalica',
    templateUrl: "myapp/categories/band.metalica.html",
    controller: "SomeController as vm"
})

I have come here 2 years later the question was asked but angular-ui-router has much proficient approach in solving this issue. It worked for nested states too.

<ul>
  <li ui-sref-active="active" class="item">
    <a ui-sref="home">Home</a>
  </li>
  <!-- ... -->
</ul>

When app navigates to home state, this is how resulting HTML will appear :

<ul>
  <li ui-sref-active="active" class="item active">
    <a ui-sref="home">Home</a>
  </li>
  <!-- ... -->
</ul>

ui-sref-active quick reference :

A directive working alongside ui-sref to add classes to an element when the related ui-sref directive's state is active, and removing them when it is inactive. The primary use-case is to simplify the special appearance of navigation menus relying on ui-sref, by having the "active" state's menu button appear different, distinguishing it from the inactive menu items.

Complete documentation.

I hope this is what you have been looking for.Place the parent url in the list class ,now whenever you navigate to child class parent class will be active

Step 1: Add a Controller for your nav bar orin your existing controller where nav bar is included add the following

app.controller('navCtrl', ['$scope', '$location', function($scope, $location) {
        $scope.isActive = function(destination) {
        return destination === $location.path();
    }

}]);

Step2: In your nav bar

<li ng-class="{active: isActive('/home')}"><a ui-sref="app.home">Browse Journal</a></li>

Thats it.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top