angular-ui / ui-router

The de-facto solution to flexible routing with nested views in AngularJS
http://ui-router.github.io/
MIT License
13.54k stars 3k forks source link

ui-sref-active not working correctly with abstract and child states #1431

Closed blah238 closed 8 years ago

blah238 commented 9 years ago

http://plnkr.co/edit/c7OS1pwI5IjAAs5cEi5s?p=preview

In the example above, clicking the Administration nav element shows a view with two tabs, Users and Roles.

These correspond to the states admin.users and admin.roles, while admin is an abstract state whose URL is inherited by admin.users. This all works fine except when setting the active class using ui-sref-active.

There are two problems in this scenario:

  1. The Administration nav element is not made active when on the Roles tab
  2. When refreshing the page (be sure to pop out the Plunker preview) while on the Roles tab, no elements get the active class set

I would like to avoid using $state.includes directly since ui-sref-active is supposed to work on child states since 0.2.11: https://github.com/angular-ui/ui-router/pull/927

Am I doing something wrong or is this a bug?

Zensavona commented 8 years ago

@sharpmachine maybe you can make reference to a beverage without privilege attached, it's oppressive.

I'd feel a lot safer if you said "I'd buy you a water"

jaman1020 commented 8 years ago

@sharpmachine we don't have water in California

nateabele commented 8 years ago

@arkin- What about extending uiSref to also accept an object?

nblasgen commented 8 years ago

@arkin- Thank you so much for your working code example. It was so simple that I had time to write this comment with my extra time.

wlkns commented 8 years ago

Ok @nateabele I'm not following, do you have an example?

nateabele commented 8 years ago

@arkin- Something similar to the example @eric-norcross posted, i.e. ui-sref-active="{ activeClass: 'active', state: 'admin.*' }" — then you'd just need to patch uiSrefActive to check whether the value is an object or string, and act accordingly. That probably just means appending the named state to states, and no-op'ing $$addStateInfo().

zloidemon commented 8 years ago

+1

andersonef commented 8 years ago

@Grievoushead Thank you so much... You solution were awesome and simple! Thanks again

Giovancruz commented 8 years ago

Sorry i am new on angular, I think I'm doing something wrong. The solution from @arkin- dont works for me. Anybody can help me? http://plnkr.co/edit/JmrZwbEJUjN3iKcrhiVl?p=preview

jwuliger commented 8 years ago

@Giovancruz and @arkin- This directive worked perfectly for me. Thanks you so much. I spent over 8 hours trying to find a working solution. I am sorry it is not working for you @Giovancruz.

brendanluna commented 8 years ago

+1 @arkin

basilinjoe commented 8 years ago

+1 @arkin

riteshjagga commented 8 years ago

@Giovancruz I'm new to plunkr so didn't know how to modify your code. I made following changes to make it working:

.state('dashboard.payments.paymentdetail', {
        parent: 'dashboard.payments',
        url:'/detail',
        templateUrl: 'paymentdetail.html',
        //controller: 'PaymentdetailCtrl',
        //controllerAs: 'paymentDetail',
        ncyBreadcrumb: {
          label: 'BILLINGDETAILS',
          parent: 'dashboard.payments'
        },
      })
MaximilianLloyd commented 8 years ago

@adamalbrecht you are a saint, thanks

axul commented 8 years ago

I made a little change to @arkin directive

.directive('uiSrefActiveIf', ['$state', '$parse', function($state, $attrs, $parse) {
    return {
        restrict: "A",
        link: function(scope, element, attrs){
            var state = attrs.uiSrefActiveIf;   

            function update() {
                if ( $state.includes(state) || $state.is(state) ) {
                    element.addClass("active");
                } else {
                    element.removeClass("active");
                }
            }

            scope.$on('$stateChangeSuccess', update);
            update();
        }
    };
}])

All I did was to move the logic from the controller to the link function in the directive to be able to get the ui-sref-active-if attr from a scope instead of a literal string

tgrant59 commented 8 years ago

It seems like people are still looking at this even though the solution is in at least the most recent version! It is still undocumented, but just use an object (like with ng-class) and it will work.

This won't work right:

<li ui-sref-active="active">
  <a ui-sref="admin.users">Administration Panel</a>
</li>

This will:

<li ui-sref-active="{ 'active': 'admin' }">
  <a ui-sref="admin.users">Administration Panel</a>
</li>
eddoliveira commented 8 years ago

@tgrant59 That worked. Thanks!

My example:

<span ui-sref-active="{'active':'details.info.medical'}"> <a href="" ui-sref="details.info.medical({param1: object1, param2: object2})"> <span>MEDICAL INFO</span> </a> </span>

samuil4 commented 8 years ago

@tgrant59 Thanks for the example.

Notes: Angular developers are still workarounding integrated features like this one in this retarded framework. THE NEW INTEGRATED FEATURES ARE NOT DOCUMENTED AT ALL!!! Every developer must go first to the official documentation where he finds nothing useful, then he goes to stackoverflow where all solutions are outdated or bad practices, then he decides that he will either waste 2 more hours going in to the comments of a related issue and explore all pull request OR simply write a custom solution that in 100% of the cases simply overrides or workarounds core features of the framework.

Thanks to the google marketing team frameworks like angular are well sold to end clients without even thinking if this framework will be a major drawback for the entire project. And that's how zillions of govnocode projects are born.

PS: wasted 3 hours to find an example of this feature and to determine if this feature is actually built in to the framework...

dayachand commented 8 years ago

I solve third level navigation issue in homer theme for angular js.

css for this:

side-menu li .nav-second-level li .nav-third-level li a{color:#6a6c6f; padding: 7px 10px 7px 53px;}

side-menu li .nav-second-level li .nav-third-level li.active a{color:#3498db}

navigation for this: here admin and admin-setup are base state

  • Admin Settings
  • phazei commented 8 years ago

    This is great:

    <span ui-sref-active="{'active':'details.info.medical'}"> <a href="" ui-sref="details.info.medical({param1: object1, param2: object2})"> <span>MEDICAL INFO</span> </a> </span>

    But what if you have a menu item that has two abstract items under it? The code does a foreach but since the class is the key, you can't have two routes use the same class.. eg) {'active':'details.info.medical', 'active':'details.records'} The unique thing here is going to be the state name, so that should be the key, or it should allow the value to be an array which wouldn't break BC. eg) {active':['details.info.medical','details.records']}

    current work around: {'active':'details.info.medical', 'active 1':'details.records'}

    riteshjagga commented 8 years ago

    What if you have nested abstract states as in the following routes organizations.organization(abstract).list/add/edit etc. and organizations.organization(abstract).users(abstract).list/add/edit etc.

    but with a flattened menu like this:

    <a class="list-group-item"
          ui-sref="home.organizations.organization.view({orgId: loggedInUser.organizationId})"
          ui-sref-active="{'active': 'home.organizations.organization', 'deactive': 'home.organizations.organization.users'}">
          Organization
    </a>
    <a class="list-group-item"
          ui-sref="home.organizations.organization.users.list({orgId: loggedInUser.organizationId})"
          ui-sref-active="{'active': 'home.organizations.organization.users'}">
          Users
    </a>

    When you are within the Users routes, both the menu items will be highlighted.

    To prevent this, added another class 'deactive': 'home.organizations.organization.users' and this deactive class can have the styles as in normal state of the menu item.

    It might be helpful for someone.

    sarahsga commented 8 years ago

    I have solved it by overriding ui-sref-active ( well, not exactly):

    angular.module('app.layout')
        .directive('uiSrefActive2', StateRefActiveDirective);
    
      StateRefActiveDirective.$inject = ['$state', '$stateParams', '$interpolate'];
    
      function StateRefActiveDirective($state, $stateParams, $interpolate) {
        return {
          restrict: "A",
          priority: 1,
          controller: ['$scope', '$element', '$attrs', function ($scope, $element, $attrs) {
            var state, params, activeClassAndStateStr;
    
            activeClassAndStateStr = $interpolate($attrs.uiSrefActiveEq || $attrs.uiSrefActive2 || '', false)($scope);
            var jsonString = activeClassAndStateStr.replace(/'/g, '"');
            var activeClassAndStateObj = JSON.parse(jsonString);
    
            $scope.$on('$stateChangeSuccess', update);
    
            // Update route state
            function update(event, toState, toParams, fromState, fromParams, options) {
    
              if (toState.name.startsWith(activeClassAndStateObj.state)) {
                $element.addClass(activeClassAndStateObj.class);
              } else {
                $element.removeClass(activeClassAndStateObj.class);
              }
    
            };
    
          }]
        }
      }

    and then using this directive in the view:

    <ion-item ui-sref="app.parent.child" ui-sref-active2="{'class':'active','state':'app.parent'}"> </ion-item>
    ffradegrada commented 8 years ago

    @arkin- works perfectly! Thank you.

    nateabele commented 8 years ago

    @samuil4 Nobody on the UI Router project works for Google. We all do this in our spare time. If you don't like the situation, figure out the issue and submit a patch to the documentation. If everyone did that just once, you wouldn't need StackOverflow.

    arshabh commented 7 years ago

    we should have the default state for the abstract state - just like react router has index route - that will solve many problems.

    owenXin commented 7 years ago

    Finally I got a solution from the following issue: https://github.com/angular-ui/ui-router/issues/2954

    ui-sref-active="{'active': 'administration.**'}"
    marioleed commented 7 years ago

    Nice @owenXin! @christopherthielen's solution did it for me as well.

    oscarr-reyes commented 6 years ago

    This doesn't work in ui-sref-active-eq

    cristianmeza commented 6 years ago

    @owenXin Thank you very much, it worked perfect.

    prashant-pokhriyal commented 5 years ago

    It seems like people are still looking at this even though the solution is in at least the most recent version! It is still undocumented, but just use an object (like with ng-class) and it will work.

    This won't work right:

    <li ui-sref-active="active">
      <a ui-sref="admin.users">Administration Panel</a>
    </li>

    This will:

    <li ui-sref-active="{ 'active': 'admin' }">
      <a ui-sref="admin.users">Administration Panel</a>
    </li>

    @tgrant59, how to dynamically set the ui-sref-active attribute. Actually I'm having it inside ng-repeat.

            <li ng-repeat="menu in top_nav_bar.menu_options" class="{{ menu.class}}"
              ui-sref="{{menu.sref}}" ui-sref-active="{'active': menu.sref + '.**'}">
              <a class="text-capitalize">{{ menu.name }}</a>
            </li>

    But ui-sref-active="{'active': menu.sref + '.**'}" is not working.