Free-for-all: Tab Helper (Summary)

Posted by Jamis Thursday, June 28, 2007 02:39:00 GMT

The first RailsWay free-for-all came off quite well. Many of you posted your favorite solutions to the problem of tab-based navigation, as posed by Nate Morse.

Jamis’ Take

Of all the solutions posted, my personal favorite was the pragmatic and simple CSS-based solution given by Mr. eel (Nate Morse came to the same solution independently):

I take a completely different approach. I ID the body of the page with the name of the current controller. Then I use a descendent CSS selector to highlight the current tab based on the body id and an id given to each link. I don’t bother with replacing the current tab link with a span. If the user wants to click that link again… then it’s the same as refreshing. Totally up to them.

With html like:

1
2
3
4
5
6
<body id="users">
  <ul>
    <li><a href="/users" id="usersNav">Users</a></li>
    <li><a href="/comments" id="commentsNav">Comments</a></li>
    <li><a href="/posts" id="postsNav">Posts</a></li>
  </ul>

I would use CSS like this

1
2
3
4
5
6
#users #usersNav,
#comments #commentsNav,
#posts #postsNav {
  background:red;
  font-weight:bold;
}

What a great approach. Although I would make the choice of the body ID explicit (rather than depending on the controller name), it is otherwise really nice. It shrugs off the whole issue of “should the current tab be a link” by saying it just doesn’t matter—every tab is always a link. Such pragmatism gets right to the heart of the Rails Way: implement just what matters, and nothing more.

Koz’s Take

A number of solutions relied on tightly coupling the controller and tabs. While this may seem like a time-saver at first, I believe that it’s unlikely to remain useful as your application grows. You’ll find yourself moving functionality into strange locations in order to make your tabs highlight correctly.

The problem is amplified with a restful application where your choice of controllers are dictated by the resources that you’re managing. You may have a list of comments in several different sections of your application, but not want to highlight the ‘comment’ tab whenever you display them.

Personally, I prefer the really simple approach of a before filter and a navigation partial.

1
2
3
4

def set_current_tab
  @current_tab = :people
end

Thanks, everyone for your submissions!

Free-for-all: Tab Helper

Posted by Jamis Monday, June 04, 2007 00:07:00 GMT

Koz and I have opinions, and we like sharing them. This site was designed as a platform for us to express those opinions.

You all have opinions, too, and some of you even have opinions that (gasp!) differ from ours. Don’t try to deny it—we read the comments on this site, too.

Well here’s your chance play the game. Nate Morse sent us an email shortly after RailsConf in which he described a problem that Koz and I would like to present to you, our readers. How would you respond?

I’ve been trying to figure out a clean way to implement a tabbed view on a website where the selected tab is written out in a <span /> element and anything unselected is a link to the particular action. So far, this is what I’ve come up with.

1
2
3
4
5
6
7
8
9
def tab_to(name, options = {})
  url = options.is_a?(String) ? options : url_for(options.merge({:only_path => false}))
  current_url = url_for(:action => @current_action, :only_path => false)
  if (url == current_url)
    content_tag(:span, name)
  else
    link_to(name, options)
  end
end

The @current_action variable is set in a before filter and used to determine if the tab I’m specifying is for the current action. Some of this code is lifted straight from the source of link_to (which is why it will accept either an options hash or a url) and is probably overkill. Also, it seems kind of hokey that I’m setting the :only_path key in a couple of places just to get the urls in a standard form. Is there a better way to do this?

Please post your responses in the comments. Because the comments aren’t editable, you should probably draft your response externally and then paste it in. You can use textile codes to format the text. For syntax-highlighted code snippets, just enclose the snippets in <macro:code> tags, with the lang attribute set to the language of the snippet (e.g., “ruby”, or “rhtml”). For example:

<macro:code lang="ruby"> def foo(a,b) return a + b end </macro:code>

At the end of the week, Koz and I will write up a summary, highlighting a few of the responses. Read, set, go!