rails
(5,585 views)

Render collection with layout

The Rails "partial layouts" feature doesn't work how I expected with collections. Here's a helper.

In Rails you can include fragments of a view template in other views, using "partial" views. Rails 2.0 introduced the feature where you can specify a "layout" template that wraps itself around the partial. This can be extremely useful when various partials render different content but you want them the share some common formatting.

For example, in a page with a sidebar with multiple boxes with different content, each box might be rendered with something like

<%= render :partial => "show", :layout => "layouts/box" %>

The views/layouts/_box.html.erb file might then include (a simplistic example):

<div class="box">
    <%= yield %>
</div> 

OK, cool, I use this sometimes.

Partials can also be called with collections (list of objects). It will automatically iterate over each one, using the partial once per object. So, for example, to render a list of objects, you'd use

<%= render :partial => "item", :collection => @items %>

When you specify a layout here, I was hoping it would use the layout each time the partial is rendered for each item. But nooooooooo! The layout is used once, yielding once to the entire list.

In other words, Rail's partial layouts does one layout for the whole collection.

Here's a simple but effective helper that does what I wanted.

def render_collection_with_layout( options )
    collection = options.delete(:collection)
    collection.collect {|object| render options.merge(:object => object) }.join
end

which is used like this:

<%= render_collection_with_layout( :partial => "item", :collection => @items, :layout => "layouts/wrapper" %>

This helper uses the layout for each object in the collection.

On a side note, the layout has access to the current item in the iteration as the local variable "object".

One caveat is this helper makes the two partial layout methods mutually exclusive. That is, its using the :layout argument for the inner layout and there's no way to specify an outer one. But that's an extreme case that I dont need.


 

Comments

by Garrett Berneche on Oct 01, 2008

>>

What version of rails does this apply too? When I call <%= render :partial =>, :collection =>, :layout => -%> I don't get results like you describe. I get the layout applied once around the collection (the way you describe the rails default behavior) but the whole thing is repeated for each entry in the collection.

by MrJaba on Dec 13, 2008

>>

Thanks for this, very useful, but I noticed it seems to ignore the :as option, so I have extended it to use this as well:

def render_collection_with_layout( options )

collection = options.delete(:collection)
locals = options.delete(:locals) || {}
as = options.delete(:as)
collection.collect do |object| 
  locals = locals.merge(as =&gt; object) if as
  render options.merge(:object =&gt; object, :locals =&gt; locals)
end.join

New Comment

markdown formatting permitted