public
Last active — forked from reagent/nav_link.rb

RAILS 3: nav_link helper for adding 'selected' class to navigation elements

  • Download Gist
README.markdown
Markdown

UPDATE: It's a gem!

This helper has finally been moved into a gem called nav_lynx!

https://github.com/vigetlabs/nav_lynx

http://rubygems.org/gems/nav_lynx

Thanks to @brianjlandau and @reagent for getting that set up and tested!

Behold, the nav_link:

The nav_link helper works just like the standard Rails link_to helper, but adds a 'selected' class to your link (or its wrapper) if certain criteria are met. By default, if the link's destination url is the same url as the url of the current page, a default class of 'selected' is added to the link.

<%= nav_link 'My Page', my_path %>

When my_path is the same as the current page url, this outputs:

<a class="selected" href="http://example.com/page">My Page</a>

For more options and full usage details, see: http://viget.com/extend/rails-selected-nav-link-helper

Drop nav_link_helper.rb into app/helpers in your Rails 3.x app and enjoy.

UPDATE: Now with block support!

Same usage as link_to:

<%= nav_link 'http://example.com/page' do %>
  <strong>My Page</strong>
<% end %>

This obviously goes beyond it, but might be good to mention the simple link_to_unless_current method available from ActionView as well.

Yeah, that was actually where I started when first looking into this issue, and is definitely good to note. The main problem with that is just doesn't output the <a> tag by default, but just the text inside it if the page is 'selected'. That tends to visually break things when css styling relies on that element's presence. You can specify specific markup to output instead, but it gets a little messy.

I learned a lot from reading through the code here, thanks for sharing, I imagine I will use it often.

nice code ...
Will it be possible to place an additional and/or Tag inside the Tag?

e.g.:

Home
or something like
More

nice code ...
Will it be possible to place an additional span-tag and/or b-tag inside the Link-Tag?

e.g.:
li --> a --> span --> b

span und b-Tag k├Ânnen auch classes haben

I'm getting errors if I use the controller_segment option and the current url does not have the segment being looked for.

It would be cool if this can accept a block as the last argument, just as link_to

Has anyone successfully used this with dropdowns? Twitter bootstrap styled dropdowns are lists nested inside the wrapper

tag, which is what needs the "active" class assigned to it.

Just updated to accept blocks!

Really nice helpers and I used this a lot. Though it cause ActionController::RoutingError if the request method is other than GET. Just updating two methods fix this issue, so please update this gist. Thanks.

      def current_controller
-      controller_for(@request.path)
+      controller_for(@request.path, @request.request_method)
     end
-    def controller_for(path)
-      Rails.application.routes.recognize_path(path)[:controller]
+    def controller_for(path, method = "GET")
+      Rails.application.routes.recognize_path(path, method: method)[:controller]
     end

This is useful. I have a couple of suggestions, though:

First, please copy the usage instructions from
http://viget.com/extend/rails-selected-nav-link-helper to README.markdown. You
never know when and if the article will become unavailable for some reason, and
it's a good idea to keep everything to do with one gist in one place.

Second, it would be nice if one could also give an option for defining a class
name to be given for unselected links, in case the user wants to define
properties for such links in a .css file.

Third, I didn't really see the point of methods that are called from only one
place in the code -- I think they can confuse rather than clarifying the code,
so I merged the code of methods link_classes, html_options and link to method
LinkGenerator::to_html.

I also made a couple of changes to parameter and variable names, for better
self-documentation and more clarity. These are the resulting changes to the
code (tested to work):

  def to_html
-   html = link
-   if @options[:wrapper]
-      html = content_tag(@options[:wrapper], html, :class => wrapper_classes)
-   end
-   html.html_safe
+   linked_classes = nil
+   if @html_options[:class]
+     linked_classes = @html_options[:class] + " #{class_name_to_be_used}"
+   elsif !@options[:wrapper_class]
+     linked_classes = class_name_to_be_used
+   end
+   merged_html_options = @html_options.merge(class: linked_classes)
+   the_link = link_to(@title, @path, merged_html_options)
+   if @options[:wrapper_class]
+     the_link = content_tag(@options[:wrapper_class], the_link, :class => all_wrapper_classes)
+   end
+   the_link.html_safe
  end

- def link_classes
-   if @html_options[:class]
-     @html_options[:class] + " #{selected_class}"
-   elsif !@options[:wrapper]
-     selected_class
-   end
- end

- def html_options
-   selected? ? @html_options.merge(:class => link_classes) : @html_options
- end

- def link
-   link_to(@title, @path, html_options)
- end

+ def unselected_class
+   @options[:unselected_class] || ''
+ end

+ def class_name_to_be_used
+   name = selected? ? selected_class : unselected_class
+ end

- def wrapper_classes
-   if selected?
-     "#{selected_class} #{@options[:wrapper_class]}"
-   else
-     @options[:wrapper_class]
-   end

+ def all_wrapper_classes
+   "#{class_name_to_be_used} #{@options[:other_wrapper_classes]}"
+ end

And this is a usage example, from a .html.erb file:

<%= nav_link "Good stuff", good_stuff_path, {}, {selected_class: 'active', unselected_class: 'inactive', wrapper_class: 'li'} %>

Thanks for the feedback guys! All good stuff. I'll be moving this into a full repo soon for better collaboration.

I think it should be nav_link_to and please make it a gem.

I tried to used it with engine mounted, unfortunately it doesn't work in such case :(

+1 @greypants for accepting blocks!

How can I make this work to ensure the when a user lands on the home-page the home tab is 'selected'?

Currently no tabs are selected when you land on the website.

Thanks.

It seems to be running smoothly also on Rails 4, so maybe the Gem dependency could be updated.

And by the way, thanks for this! :-)

Just what I needed, and working great on Rails 4! In a similar fashion to how url_segment works, Is it possible to add the selected class to all pages under a sub-directory structure ?

For example: admin/products/item, admin/products/list etc.. Automatically adding the "selected" class to all urls within "/products" (within the same controller)

Please sign in to comment on this gist.

Something went wrong with that request. Please try again.