Skip to content

ui-active-highlight directive #19

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
timkindberg opened this issue Feb 16, 2013 · 31 comments · Fixed by #635
Closed

ui-active-highlight directive #19

timkindberg opened this issue Feb 16, 2013 · 31 comments · Fixed by #635
Assignees
Labels
Milestone

Comments

@timkindberg
Copy link
Contributor

@ksperling said somewhere at some point:

Finally, to provide utility directives around ... highlighting the current place in the UI ...

I propose a directive called ui-active-highlight, it would add an "active" class onto the element whenever the state in the parameter was active.

<li ui-active-highlight="contacts">
    <a href="/contacts">Go to Contacts Page</a>
</li>

Refer to the ui-route directive authored by @ProLoser

@ProLoser
Copy link
Member

I agree that the directive name is bad, I do not mind changing it to be more abstract so that it is applicable to the new router.

@ksperling
Copy link
Contributor

It would be very neat if this could be combined with ui-state-href (whatever syntax that may end up with), so you could say something like

<a ui-state-ref="contacts.detail(contactId:$contact.id)" highlight>...</a>

which would highlight the link if the state linked to is (or is included in) the currently active state. This would imply that the match would be on state AND parameters.

In either case it should be possible to specify if we're asking for an exact "is" state match or an "includes" match (i.e. the named state or any of it's descendants).

I think this simplified 'make a link and highlight it' probably covers about 90% of cases, so maybe we can even leave any more complex cases to simply call $state.is() or $state.contains() like the sample app currently does.

The only fly in the ointment is that fairly often you want the 'active' (or equivalent) CSS class on a parent of the link, not the link itself. I wonder if something like ... highlight="li:active" ... to add class 'active' to the nearest 'li' ancestor would be too hacky to implement. That would definitely make the vast majority of nav linking / highlighting work a breeze.

@timkindberg
Copy link
Contributor Author

@ksperling 👍

@lrlopez
Copy link

lrlopez commented Feb 24, 2013

Nice, I like it. Wouldn't be ui-highlight more appropiate?

<a ui-state-ref="contacts.detail(contactId:$contact.id)" ui-highlight>...</a>

@ProLoser
Copy link
Member

Do not implement this as a hilighter. There are reasons I developed
ui-route the way I did. Please use it as a base to start from.
On Feb 24, 2013 6:10 AM, "Luis Ramón López" [email protected]
wrote:

Nice, I like it. Wouldn't be ui-highlight more appropiate?


Reply to this email directly or view it on GitHubhttps://github.com/angular-ui/router/issues/19#issuecomment-14008881.

@ksperling
Copy link
Contributor

@ProLoser would you mind going into a bit more detail on why a highight directive or highlight option on ui-state-ref is a bad idea? It certainly seems like an extremely common case to me, so it would be nice if it was supported with the minimum amount of fuss for the developer.

@ProLoser
Copy link
Member

So 'highlight' is addressing only one fixed use-case. There are also other issues with the suggested implementation.

Lets say we have a nav and want the active item.

You proposed:

<a ui-ref-highlight="some.path">...</a>

This is fine, except that you are now stuck to that one tag, using fixed CSS classes, and can only toggle classes.

What if I have a normal nav list and want the class on the LI? What if I want to access this behavior and use it for validation, or other checks. What if I want to do these checks from descendant elements?

<li><a>...</a></li>
<li><a>...</a></li>
<li><a>...</a></li>

Instead, ui-route was designed to be use-case agnostic.

<li ui-route="some.path" ng-class="{'active':$isRoute}">
  <a ng-show="$isRoute">
  ...
  <input ng-required="$isRoute">

Wether you use the flag to trigger a class state or visibility or validation is easily up to the developer, and the scope variable can be accessed from grandchildren. It also hooks into the href and ng-href properties if they are found on the same tag.

Now, I understand concerns with the name ui-route but if that's really the major concern we can rename it to something like ui-active-path or ui-active-state or something like that. The point is that I don't want the assumed connotation to be a class toggle.

@timkindberg
Copy link
Contributor Author

Dean I think your directive is really cool and robust. I'll be honest that I had not had time to really look at it. It's clear that you've been down this road already and have already solved this issue. As long as we can bundle your route directive with the rest of the code in a way that makes it feel like part of the same family then I have a yes vote for integrating it.

Sent from my iPad

On Feb 24, 2013, at 10:33 PM, Dean Sofer [email protected] wrote:

So 'highlight' is addressing only one fixed use-case. There are also other issues with the suggested implementation.

Lets say we have a nav and want the active item.

You proposed:

...
This is fine, except that you are now stuck to that one tag, using fixed CSS classes, and can only toggle classes.

What if I have a normal nav list and want the class on the LI? What if I want to access this behavior and use it for validation, or other checks. What if I want to do these checks from descendant elements?

  • ...
  • ...
  • ...
  • Instead, ui-route was designed to be use-case agnostic.
  • ... Wether you use the flag to trigger a class state or visibility or validation is easily up to the developer, and the scope variable can be accessed from grandchildren. It also hooks into the href and ng-href properties if they are found on the same tag.

    Now, I understand concerns with the name ui-route but if that's really the major concern we can rename it to something like ui-active-path or ui-active-state or something like that. The point is that I don't want the assumed connotation to be a class toggle.


    Reply to this email directly or view it on GitHub.

  • @ksperling
    Copy link
    Contributor

    Hmm... from what I understand by looking at ui-route it's essentially a way of looking at $location and setting a boolean in $scope when the location matches a pattern. However with the current $state code, that shouldn't really be necessary anymore -- the URL maps to a state, and then any decisions in the UI should be able to be made based on the state and it's parameters, without going back to looking at $location directly. (I don't think I understand how it's meant to be used with href / ng-href.)

    Using ui-route="some.state" ... $isRoute is the same as just using $state.is('some.state'), which is pretty easy to understand and more concise, and easily combines with additional conditions, e.g. $state.is('some.state') && $stateParams.id == 3. So I think the generic use cases are essentially already covered without a directive, unless you think assigning it to local scope property is the key feature.

    I was looking for a way to replace

    <li ng-class="{ active: $state.includes('contacts.detail') && $stateParams.contactId == contact.id }">
      <a ui-ref="contacts.detail" params="{ contactId: contact.id }">>{{contact.name}}</a>
    </li>
    

    with this (1)

    <li>
      <a ui-ref="contacts.detail" params="{ contactId: contact.id }" highlight="li:active">{{contact.name}}</a>
    </li>
    

    or maybe this (2)

    <li ui-ref="contacts.detail" params="{ contactId: contact.id }" highlight="active" as="href">
      <a  href="{{href}}">{{contact.name}}</a>
    </li>
    

    I.e. I want to avoid having to repeat the state name and parameters, while being able to control which element and CSS class are used for highlighting (and have reasonable defaults for those, e.g. 'active' for the CSS class and same element).

    I think something along the lines of (1) is more intuitive, because the focus is on the link being generated, and the highlighting is an add-on, whereas in (2) it's more difficult to see what is going on.

    @ProLoser
    Copy link
    Member

    I figured with how states worked this would be mostly useless. However the proposal of "li:active" etc is just a bad solution and will lead to confusion. Instead, I think that simply creating a <a ui-ref> directive is good enough.

    Do me a favor, and hold off on the highlighting directive for now.

    Dean Sofer
    DeanSofer.com
    714.900.2254

    On Sunday, February 24, 2013 at 8:21 PM, Karsten Sperling wrote:

    Hmm... from what I understand by looking at ui-route it's essentially a way of looking at $location and setting a boolean in $scope when the location matches a pattern. However with the current $state code, that shouldn't really be necessary anymore -- the URL maps to a state, and then any decisions in the UI should be able to be made based on the state and it's parameters, without going back to looking at $location directly. (I don't think I understand how it's meant to be used with href / ng-href.)
    Using ui-route="some.state" ... $isRoute is the same as just using $state.is('some.state'), which is pretty easy to understand and more concise, and easily combines with additional conditions, e.g. $state.is('some.state') && $stateParams.id == 3. So I think the generic use cases are essentially already covered without a directive, unless you think assigning it to local scope property is the key feature.
    I was looking for a way to replace

  • >{{contact.name (http://contact.name)}}
  • with this (1)
  • {{contact.name (http://contact.name)}}
  • or maybe this (2)
  • {{contact.name (http://contact.name)}}
  • I.e. I want to avoid having to repeat the state name and parameters, while being able to control which element and CSS class are used for highlighting (and have reasonable defaults for those, e.g. 'active' for the CSS class and same element). I think something along the lines of (1) is more intuitive, because the focus is on the link being generated, and the highlighting is an add-on, whereas in (2) it's more difficult to see what is going on.


    Reply to this email directly or view it on GitHub (https://github.com/angular-ui/router/issues/19#issuecomment-14025194).

    @timkindberg
    Copy link
    Contributor Author

    If anyone wants a directive for this until we figure out what we are gonna do with it, check out ngBoilerplate's activeIfCurrent directive. Would probably be pretty easy to modify for states.

    https://github.com/joshdmiller/ng-boilerplate/blob/master/src/components/activeIfCurrent/activeIfCurrentDirective.js

    @ProLoser
    Copy link
    Member

    @timkindberg I'm not sure how that's better than ui-route? You could simply copy it over and rename it, even though this entire project is a state machine but it's called ui-router

    @ProLoser
    Copy link
    Member

    If you would like to go ahead and implement this directive, go ahead. But please use the pattern set out in uiRoute and not in the ngBoilerplate as it is a less versatile solution.

    @timkindberg
    Copy link
    Contributor Author

    Yeah I wasn't saying it was better but that it was an option. I'm not feeling strongly about it. I just stumbled across the directive at Ng boilerplate and thought looked good for a quick fix until we had figured out what we were gonna do. Last I heard you asked Karsten to hold off so I thought something was in the works. I figured once it was finished that would be the official way to do it. Sorry if I misunderstood.

    @ProLoser
    Copy link
    Member

    No nothing was in the works, I was just busy and wanted to review the
    design. For now if you guys have the time go ahead and implement it, but I
    just wanted to make sure it was designed as a scope flag and not a class
    toggle.

    I realize I probably sound obsessive at this point.
    On Mar 15, 2013 2:40 PM, "Tim Kindberg" [email protected] wrote:

    Yeah I wasn't saying it was better but that it was an option. I'm not
    feeling strongly about it. I just stumbled across the directive at Ng
    boilerplate and thought looked good for a quick fix until we had figured
    out what we were gonna do. Last I heard you asked Karsten to hold off so I
    thought something was in the works. I figured once it was finished that
    would be the official way to do it. Sorry if I misunderstood.


    Reply to this email directly or view it on GitHubhttps://github.com//issues/19#issuecomment-14986386
    .

    @timkindberg
    Copy link
    Contributor Author

    Ha ha that's ok. I don't have time at the moment either. Hence the half
    assed post to a short term solution.

    @lrlopez
    Copy link

    lrlopez commented Apr 11, 2013

    Is the right time to rethink the issue?

    About @ProLoser's approach of using something similar to this:

    <li ui-route="some.path" ng-class="{'active':$isRoute}">
      <a ng-show="$isRoute">
      ...
      <input ng-required="$isRoute">

    I have some concerns on the implementation, as it relies on the scope inheritance: This means that it could not work when using directives with isolated scopes, and end-users may not know why... Any thought on this?

    Anyway, if you think that it is ok I could try to implement it (although using ui-state and $isState instead)

    @ProLoser
    Copy link
    Member

    Your point about scope isolation is applicable to ANY scope variable. This includes $index, or iterators, etc. If the developer is using scope isolation and they want to access scope variables they should explicitly list every variable they want access to. Otherwise, I do not think that your concern is valid.

    Dean Sofer
    DeanSofer.com
    714.900.2254

    On Thursday, April 11, 2013 at 2:54 PM, Luis Ramón López wrote:

    Is the right time to rethink the issue?
    About @ProLoser (https://github.com/ProLoser)'s approach of using something similar to this:

  • ...

    I have some concerns on the implementation, as it relies on the scope inheritance: This means that it could not work when using directives with isolated scopes, and end-users may not know why... Any thought on this?
    Anyway, if you think that it is ok I could try to implement it (although using ui-state and $isState instead)


    Reply to this email directly or view it on GitHub (ui-active-highlight directive #19 (comment)).

  • @ksperling
    Copy link
    Contributor

    What I don't quite see is how having a separate directive is better than just calling $state.is('some.state') or $state.contains('some.state') from the template. So essentially we'd be creating a directive to assign the result of a function call to a hard-coded variable name.

    Maybe if we had <li ui-ref="some.path" params="..."> introduce both $href and $isRef into the scope, we'd have one directive that can be used for both link generation AND highlighting (or other things you might want to do with that flag)

    @ksperling
    Copy link
    Contributor

    To clarify my previous comment, I think one key feature for a directive to "carry it's weight" in these scenarios is to be able to avoid the repetition of state name and parameters for the case of generating and highlighting the same link.

    @ProLoser
    Copy link
    Member

    Huh? You mean you don't want to have to specify the url it needs to look for? Or are you referring to being able to change $isRoute to something else?

    @ProLoser
    Copy link
    Member

    Oh sorry I missed your second comment before responding. I agree, if people can do $state.is('some.state') then the relevance of this directive is diminished. I think your proposal to have ui-ref do both $isRef and $href is a good idea, although I did not realize it merely provides $href instead of doing href="" for you

    @ksperling
    Copy link
    Contributor

    Ideally we could let the user choose between doing href='' for you or not, so for the case where you want to e.g. highlight an LI, you'll have the directive on the LI, and use {{$href}} on the nested A.

    Maybe if ui-ref looks at the element, and if it is an A and has no explicit href attribute, then set the href attribute as well?

    @mrrooijen
    Copy link

    Is there currently a solution to doing this conditional active css classing on html nodes based on the current route? I was using $uiRoute from AngularUI before I switched to states and after switching it appears this functionality only works on initial page load, but not during state transitions once the app is loaded.

    Is there any way to fix this? Or is there an alternative solution to this problem that's specific to this states module?

    @ProLoser
    Copy link
    Member

    $state.contains('some.state')

    @mrrooijen
    Copy link

    Nice, thanks! Is there a way to do this when the target URL contains parameters? E.g.

    /accounts/:account_id/users/:user_id/recent_activity
    

    Say you have a list of users and you click one, and that list stays on screen while the user's recent_activity controller/view gets shown, and you just want to highlight the "selected" user by user_id in that sidebar list. How would you accomplish this?

    Thanks again!

    EDIT: Never mind, figured you could use a combination of $state and $stateParams to narrow it down. Is there a recommended way to expose $state and $stateParams to the $scope? Or would you just create for example a MainController that injects $state and $stateParams and assigns them to $scope.$state and $scope.stateParams?

    @ksperling
    Copy link
    Contributor

    You can publish $state on $rootScope with a module config function for example, otherwise a controller will do just fine as well.

    @caitp
    Copy link
    Contributor

    caitp commented Dec 5, 2013

    I think we can call this closed by ae360fc

    @timkindberg
    Copy link
    Contributor Author

    10 months in the making...

    @hcliff
    Copy link

    hcliff commented Dec 8, 2013

    It would be useful to use this on abstract states (e.g, highlight a user icon if I'm viewing a list of users or an individual user)

    @nateabele
    Copy link
    Contributor

    @hcliff Yup, we'll get there eventually. Any time you wanna pony up with a PR, I'll be happy to give you some pointers.

    Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
    Labels
    Projects
    None yet
    Development

    Successfully merging a pull request may close this issue.

    9 participants