Monday, November 30, 2009

Clearing out content in content_for

In rails, multiple calls to content_for append their body's together and yield the concatenation of all the calls. Conceptually, this looks something like:

<% content_for :title do %>
Hello
<% end %>
<% content_for :title do %>
World
<% end %>


producing "Hello World" when you call
<% yield :title %>


In my current project, I was using redbox to create popup windows (see next post for details). In some cases, I wanted to have multiple popup windows on the same page. However, when I created those popups, each partial had a content_for :title and I was ending up with later popups having titles that contained all the words from previous titles.

After a little digging, I decided I needed a version of content_for that reset the value rather than appending to it. I found some old patches for rails that changed how content_for worked, but I thought it would be cleaner to have a new method that set the content_for variable rather than patching the version in rails.

#config/initializers/set_content_for.rb
module ActionView
module Helpers
module CaptureHelper
def set_content_for(name, content = nil, &block)
ivar = "@content_for_#{name}"
instance_variable_set(ivar, nil)
content_for(name, content, &block)
end
end
end
end


by putting this in /config/initializers it's automatically loaded by rails at startup. You use it just like you'd use content_for
<% set_content_for :title do %>
...whatever...
<end %>


but it clears out the current value of the section before setting it to the content of your new block.

Using a modal layout with redbox for popup windows

In my current project, I've been using redbox for popup windows. By default, these come up as a plain white box, so I wanted to style them with a colored area for a title, a region at the bottom for buttons, etc. So I created a layout that has all the styles

#app/views/layouts/_modal.erb
<div class="modal_box">
<div id="title">
<h3>
<%= yield :title %>
<span><%= link_to_close_redbox 'close' %></span>
</h3>
</div>
<div id="modal_flashes">
<%= render :partial => '/layouts/flashes' %>
</div>
<%= yield %>
</div>


then to create a modal window, I just create a partial with the content I want to appear in the modal window

#app/views/wizard/_advice.html.erb
<% content_for :title do %>
How to...
<% end %>
<div class="modal_content content_section">
<p>As the ...</p>
</div>
<div class="actions">
<%= link_to_close_redbox 'Close', :class => "modal_action_link" %>
</div>


and render that partial in a hidden div on my main view

#app/views/wizard/page1.html.erb
<div id="advice" style="display:none;">
<%= render :partial => 'advice', :layout => '/layouts/modal' %>
</div>


that is pulled by by a link_to_redbox call
<%= link_to_redbox 'advice?', 'advice' %>


In this case, I could have gone farther and pushed more of the styling & divs into the modal template. Then I would have had additional content_for's for :body and :actions. I didn't do that because in other places I needed to have the the popups display forms which included content in the body and submit buttons down in the actions. By making everything other than the title the be the content that was yielded, that was easy to do.