
Let's say I have a webapp that can be broken into 3 broad categories of activity: Lies, Damn Lies, and Statistics. My webapp has a tab for each of these categories. When I am on the page that tells Lies I want the Lies tab brought to the foreground. When I am creating a new Damn Lie I want the Damn Lies tab highlighted, etc. This is basic webapp stuff. In the rails universe many gems have been written to simplify this, but unless you have complex requirements I call YAGNI! I worked out a simple solution for this and thought I would share.
Diving right into the code let's look at the markup that creates the tabs. I keep it in the toplevel application layout, so it appears on all pages of the application:
<% yield :selected_navigation %>
<div id="container">
<div id="tabs">
<ul>
<li class="<%= selected_navigation(:lies) %>">
<%= link_to "Lies", lies_path %>
</li>
<li class="<%= selected_navigation(:damn_lies) %>">
<%= link_to "Damn Lies", damn_lies_path %>
</li>
<li class="<%= selected_navigation(:statistics) %>">
<%= link_to "statistics", statistics_path %>
</li>
<li class="logout">
<%= link_to "Logout", "/logout" %>
</li>
</ul>
</div>
</div>
First, notice the yield for :selected_navigation. Views in the application implement :selected_navigation and use it to set a variable that maps a page to a particular tab. Here is a snippet from a page that maps to the :damn_lies top navigation element:
<% content_for :selected_navigation do%>
<% @selected_navigation = :damn_lies %>
<% end %>
The final piece of the puzzle is the selected_navigation helper that is being called for each li in the layout. It compares the current page's navigation mapping @selected_navigation to the desired tab. If it finds a match the "current" class is added to the tab. And that gives the user a visual cue as to which tab we are currently on. Here is the code for the helper:
def selected_navigation(element)
element == @selected_nav_element ? "current" : ""
end
This is a lightweight solution. I like it because it doesn't require you to put anything in the controller, or to have some annoying configuration file that maps pages or controllers to tabs. So how do you usually do this?