1
votes

I'm trying to render a collection of different objects in a same format for each. I want to keep it DRY, so I want to use partials and partial layouts.

Edit - brief clarification : That I need is not to display common items on all publication pages, but to display common properties/fields on each item. This is why I need partial layouts e.g. a layout for the the partial, not a layout for the page.

I have a collection of different objects :

@publications = Publications.all
# Publication is the parent class
# @publications = [ImagePost, VideoPost, TextPost, ...]

I want to render all publications in a list. Each publication have some common properties : author, date, ... I want to put this properties in a partial layout.

So in my view, to render the collection, I do :

<%= render :partial => 'publications', :locals => {:publications => @publications} %>

In the first level partial views/publications/_publications.html.erb, I loop on the item and try to render each item in its partial and with a common partial layout :

<ul class='publications_list'>
  <% publications.each do |p| %>
    <%= render p, :layout => 'publications/publication_layout' %>
  <% end %>
</ul>

The partial layout, views/publications/_publication_layout.html.erb :

<li>
  <h2><%= link_to publication.title, publication %></h2>
  ... Other common properties that I want to display on each item, independently of its type ...
  <p><%= yield %></p>
</li>

And finally for each object type, I have a partial (e.g. image_posts/_image_post.html.erb and so) containing the code to display properly each.

My problem : I don't manage to render each publication in the common partial layout publication_layout. This layout is simply ignored by rails. Each item is correctly rendered but without this layout which include the common properties and the <li> tag.

Any suggestion about why my partial layout is ignored ?

3
Did you tried to specify :partial directly, as in your guide proposed? "Also note that explicitly specifying :partial is required when passing additional options such as :layout.".Mark Huk
See apidoc for extended documentation.Mark Huk
Well spotted, this can explain the whole thing... This behaviour is really strange and not intuitive. And this is not very convenient for my case, because of the polymorphic collection (the partial depends of the type of p). Any idea on how to retrieve the good partial for each object in an elegant and short way ?Thomas Guillory
@MarkGuk So I've found my workaround, see my answer for details. Thanks for your contribution. Make an answer if you want to claim the bounty.Thomas Guillory

3 Answers

2
votes

ANSWER AND WORKAROUNDS

Thanks to @MarkGuk to have spotted this line in the doc :

Also note that explicitly specifying :partial is required when passing additional options such as :layout.

So it's just not possible to simply render a polymorphic collection within the same partial for each item.

Workaround 1 : I first tried to compute partial path for each item, store it in the model for convenience, and so render each item in the good partial with the good layout. But I realize that this method, I can't refer to the object publication inside the layout...

<ul class='publications_list'>
  <% publications.each do |p| %>
    <% # p.partial = p.class.to_s.underscore.pluralize +'/'+ p.class.to_s.underscore %>
    <%= render :partial => p.partial, :object => p, :as => :publication, :layout => 'publications/publication_layout' %>
  <% end %>
</ul>

Workaround 2 : Finally I used nested partials.

<ul class='publications_list'>
  <% publications.each do |p| %>
    <%= render :partial => 'publications/publication_layout', :object => p, :as => :publication %>
  <% end %>
</ul>

and replaced yield with a render publication inside the layout.

0
votes

I wonder if a nested layout might serve you better here. The guide should point you in the right direction and I found it trivially easy to get working, but as a start:

In views/layouts/application.html.erb, change yield to:

<%= content_for?(:publication_content) ? yield(:publication_content) : yield %>

Eliminate the partial views/publications/_publication.html.erb and instead create the nested layout views/layouts/publication.html.erb:

<% content_for :content do %>
    # Put common items here
    <%= content_for?(:content) ? yield(:content) : yield %> # this is for specifics
<% end %>

<%= render :template => 'layouts/application' %>

Then specific layouts can be nested further or specified with additional tags in the view, depending on the rest of your setup.