I recently encountered a scenario where I needed to dry up some HTML code for a navigation snippet that needed to appear on both the top and bottom of my main content. Here’s a snipped version of the code
<div class="tablenav top row"> <div class="somenavigationfunction"></div> <div class="anotherfunction"></div> <!-- Some code for the navigation here --> </div> <div class="display row"> <!-- Main display page here --> </div> <div class="tablenav bottom row"> <div class="somenavigationfunction"></div> <div class="anotherfunction"></div> <!-- Some code for the navigation here --> </div>
The code that I need repeated divs with tablenav classes. The usual way to go about this is using content_for and yield:
<% content_for :tablenav do %> <div class="tablenav top row"> <div class="somenavigationfunction"></div> <div class="anotherfunction"></div> <!-- Some code for the navigation here --> </div> <% yield :tablenav %> <div class="display row"> <!-- Main display page here --> </div> <% yield :tablenav %>
A bit DRY-er now right? The problem with this approach is that both instances of the yielded output sports the same set of classes. This is good most of the time but in my current usage, I need the top instance to use a “top” class while the bottom one needed a “bottom” class for proper padding. I could simply add another div to wrap each of the yield lines but that’s requires me to edit the css file to reflect the change. Here’s the solution I came up with using content_for and helpers:
<% content_for :tablenav do %> <div class="tablenav top row"> <div class="somenavigationfunction"></div> <div class="anotherfunction"></div> <!-- Some code for the navigation here --> </div> <%= nav_block("top") %> <div class="display row"> <!-- Main display page here --> </div> <%= nav_block("bottom") %>
And the code for the helper
def nav_block(location = nil, &block) content_tag("div", content_for(:tablenav), :class => "tablenav #{location} row").html_safe end
Just fire up the page in your browse and watch it work :). Now how exactly did this works? Turns out that content_for can also be used to output it’s contents. If you’re wondering why yield wasn’t used is because it simply doesn’t work with helpers. If you tried to use yield, you’ll end up with the following LocalJumpError: no block given (yield).
Important Note: I’ve only tested this on Rails 3.x, Rails 3.2 to be exact.
Hope that helps.