
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?
1 comment:
We do largely the same thing, but with more helper code.
In our application, the tabs are rendered via a helper methods to remove a lot of the noise. The end result is tab definitions that look like this. The helper methods dump out the appropriate markup.
<% tabs do -%>
<%= tab :foos, 'Foos', foos_path %>
<%= tab :bars, 'Bars', bars_path %>
<% end -%>
Controllers are implicitly under tabs that align with their name (eg. FoosContoller is under the foos tab) or they can be set explicitly (tab 'foos').
I like your approach and if we didn't already have this code, I might consider it for future efforts. I'd probably still keep the tab setting as a controller responsibility as we tend to put whole controllers under a single tab. Also, I believe the whole content_for approach is unnecessary if all you're setting is a variable. Rails renders your templates inside out, so any variables defined in your template are available for use in layouts.
Post a Comment