Render partial in Ruby on rails a collection is multiplying items - ruby-on-rails

I want to display a list of items in a page in Ruby-on-Rails. I use partials
in my index.html.erb file I have:
<%= #lista = News.find(:all, :order => Document::COL_DATE + ' DESC, id DESC')
render :partial => "newsitem",
:layout => "list_news",
:spacer_template => "spacer",
:collection => #lista
%>
in _list_news.html.erb I have:
<div class="news">
<%= yield %>
</div>
in _spacer.html.erb I have <hr/>
in _newsitem.html.erb I have
<%= newsitem_counter + 1 %>
<!-- Code to print details for one item -->
The problem is that it prints the list multiple times:
If the list has 3 items, it shows them 3 times: 1,2,3,1,2,3,1,2,3.
If it has 7 items, those items are printed 7 times.
What is wrong in my code?

The :layout option is usually used with :action or a single :partial, not with :collection's. The problem: yield is being called for every item in the list.
You'd have to look at the sources to figure out why :layout and :collection are acting this way; but suffice it to say your code should probably just be rewritten so that it doesn't rely on :layout and :collection working together.
Here's one way you could do so, with the assumption that reusing this code in other views is a high priority. Unless you're using lots of caching, rendering each partial tends to be fairly slow, especially if your news_feed has many items, so I've consolidated it into one.
controller/news_controller.rb
class NewsController < ApplicationController
def index
#news_feed = News.find(:all,
:order => Document::COL_DATE + ' DESC, id DESC')
end
end
views/news/index.html.erb
<%= render :partial => "news_feed",
:locals => { :news_feed => #news_feed} %>
views/news/_news_feed.html.erb
<ul class="news">
<% news_feed.each_with_index do |news_item, news_item_counter| %>
<li>
<%= newsitem_counter + 1 %>
<%# Code to print details for one item %>
</li>
<% end %>
</ul>
If rendering a whole bunch of partials is okay for you running-time wise, you might find this implementation of views/news/_news_feed.html.erb nicer:
<div class="news">
<%= render :partial => 'news_item', :collection => news_feed, :spacer_template => "horizontal_break" %>
</div>
views/news/_news_item.html.erb
<%= newsitem_counter + 1 %>
<%# Code to print details for one item %>
views/news/_horizontal_break.html.erb
<hr />
So instead of rendering :layout, you render one big partial which wraps the collection.

This is a known problem in Rails 2.3.8 and Rails 3! https://rails.lighthouseapp.com/projects/8994/tickets/2279-render-layout-with-block-and-multiple-yields

Related

How can I display a list of child records?

I have a table of accounts and venues where an account can have many venues.
I'm displaying all the accounts as partials on the accounts index page and would like for each one to include the names of the venues linked to them.
Heres what I have:
account partial
<%= link_to free_account do %>
<div class="account_partial">
<span class="account_header"><%= free_account.name %></span> - <span class="free_account_highlight">(<%= free_account.role %>)</span><br>
<%= render :partial => 'venues/account_venue', :collection => #account.venues %>
</div>
<% end %>
account_venue partial
<%= venue.name %>
I'm getting this error:
NoMethodError in Accounts#index
undefined method `venues' for nil:NilClass
Extracted source (around line #12):
12: <%= render :partial => 'venues/account_venue',
:collection => #account.venues %>
Any help would be much appreciated!
edit
accounts_controller
class AccountsController < ApplicationController
load_and_authorize_resource
def index
#accounts = Account.all(:include => :venues)
end
end
accounts index.html.erb
<div id="narrow_container">
<div class="free_accounts_container">
<h2 class="show_orange">Free accounts</h3>
<%= render :partial => 'free_account', :collection => #accounts %>
</div>
<div class="premium_accounts_container">
<h2 class="show_orange">Premium accounts</h3>
<%= render :partial => 'premium_account', :collection => #accounts %><br><br>
</div>
<div class="clearall"></div>
<div class="button">
<%= link_to 'add account', new_account_path %>
</div>
</div>
_free_account.html.erb
<%= link_to free_account do %>
<% if free_account.role == "free" %>
<div class="account_partial">
<span class="account_header"><%= free_account.name %></span> - <span class="free_account_highlight">(<%= free_account.role %>)</span><br>
<div class="owner_details">
<span class="pre_account_highlight">Owners username:</span><span class="account_highlight"><%= free_account.user.username %></span>
<span class="pre_account_highlight">Owners e-mail:</span><span class="account_highlight"><%= free_account.user.email %></span>
</div>
<div class="account_details">
</div>
<%= render :partial => 'venues/account_venue', :collection => #account.venues %>
</div>
<% else %>
<% end %>
<% end %>
update
If I change the partial call to:
<%= render :partial => 'venues/account_venue', :collection => #accounts %>
and the account_venue partial to just read 'test' it loads without error but displays the word test 4 times (theres 4 account records) if I add a new account record it displays the word test 5 times.
You need to set #account to something in your controller, and that model instance should be joined to (or include) the Venues model. Like so:
#account = Account.find(params[:id], :include => :venues)
Edit: I take it you have already set up your Account and Venue models with has_many and belongs_to relationships?
Edit two: I see now that you're trying to access Accounts#index, in which case the code above should be changed to something like (since we're not looking at one specific account):
#accounts = Account.all(:include => :venues)
Edit three: Now that you've posted the controller and partials code as well, a couple of things stand out; When rendering a partial using a collection the resulting object inside the partial derives its name from the partial and not the collection. From the Rails Guides:
"When a partial is called with a pluralized collection, then the
individual instances of the partial have access to the member of the
collection being rendered via a variable named after the partial."
In your partial _account_venue.html.erb you have <%= venue.name %> - this needs to be changed to <%= account_venue.name %>.
Secondly, in _free_account.html.erb, where you call the account_venue partial, you're referring to a collection object named #account - where does this come from? Since the free_account partial is also called with a collection, the object you should be using will be called free_account - indeed you are referencing it by this name earlier in the same partial when you do <%= free_account.name %>. So the render partial call should look like this:
<%= render :partial => 'venues/account_venue', :collection => free_account.venues %>
Hope this helps!
Looks like your not assigning #account to anything. Should this be free_account instead?

Rails 3 and partials layouts

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 ?
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.
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.
See comments to the question and marked answer from the author.
Documentation
Answer from the author

Ruby error undefined method `model_name' for NilClass:Class using scopes

Using rails 3.1, ruby 1.92. I have a bunch of products in a store: movies, music, and books. I have three scopes in my products controller, all called the same (movies, music, and books). The point of our assignment is to have the index of the product controller show all items on load of the site. No problem. We then have to link_to books, music, or movies, and have the appropriate products shown. Also done.
However, I now have three files in my views, and I need to make a partial to display all of these. I have used this:
<% #products.each do |product| %>
<div class="entry">
<%= image_tag(product.image_url) %>
<h3><%= product.title %></h3>
<%= sanitize(product.description) %>
<div class="price_line">
<span class="price"><%= number_to_currency(product.price) %></span>
<%= button_to 'Add to Cart', line_items_path(:product_id => product) %>
</div>
</div>
<% end %>
I don't really understand what is happening. I have taken what is originally a '#product' and with the use of my scope, it is now #book, or #music, etc. In my code with the three files, I have this code, but instead of '#products' and 'do |product|', I have #books do |book| etc.
What do I need to do so the partial will be able to be functional using #books, #music, and #movies? Each time I pass these variables I get the NilClass error. How do I go about this?
Check out the rails guide on layouts and rendering - especially the section on partials, which I linked you directly to.
Basically, you want to refactor the code to avoid using #products, #books, #movies, etc, and make a partial that uses a local variable.
In your products view:
<%= render :partial => "whatever_you_name_it", :locals => {:products => #products} %>
In your books view:
<%= render :partial => "whatever_you_name_it", :locals => {:products => #books} %>
In your movies view:
<%= render :partial => "whatever_you_name_it", :locals => {:products => #movies} %>
And inside the partial, just change your #products.each do |product| to products.each do |product|
If I understand well your problem you need something like :
render :partial => 'partial_name', locals => {:items => #books}
And, in your partial, you can do:
items.each do |item| ...

Rails rendering a partial multiple times based on the collection size

I have a partial that takes a collection, iterates through that collection displaying the individual items.
Here is the code:
The Partial:
<% for point in #points %>
<div id="im" style="float:left;width:80px;height:90px;padding:5px;border:solid 1px #D3D3D3; margin:5px; ">
<img width="80" height="80" src="\uploaded\<%= point.id %>.gif" />
<%= link_to_remote " Delete", :url => { :action => "delete_point", :id => point.id }, :before => "Element.show('spinner')", :complete => "Element.hide('spinner')" %>
</div>
<% end %>
The rjs from the controller:
page.replace_html :points_partial, :partial => 'points', :collection=>#points
For some reason the partial is rendered by the amount of items in the collection. If there are ten items in the collection then the partial is rendered then times.
This guy had a similar problem but it was related to layouts.
Render partial in Ruby on rails a collection is multiplying items
It is driving me mad because it should be simple and all the other partials work without any difficulty.
The :collection causes the controller to iterate over #points and render the partial once for each item in the collection. If you set up the partial to render just one point, the controller code should work as expected.
BTW, in Rails 3 you can use <%= render #points %> as a shortcut for <%= render :partial => "points/point", :collection => #points %>
the :collection parameter is telling the partial "display the partial for each member of #points", and then your partial iterates again through the same collection.
In your partial, just get rid of the wrapping for point in #points loop and it should work fine.
See here for more info: http://guides.rubyonrails.org/layouts_and_rendering.html#rendering-collections

Rails generate extra <li> tags automatically

this is the code :
<ul >
<% items.each do |item|%>
<%= render :partial => "somepartial", :locals => { :title => item.title} %>
test_text
<% end %>
</ul>
the partial:
<li><a><%= title %></a></li>
and the out put is :
<ul >
<li><a>item1</a></li>
<li>test_text</li>
<li><a>item2</a></li>
<li>test_text</li>
<li><a>item3</a></li>
<li>test_text</li>
</ul>
< li > tags around the test_text is extra. Partial and the model is not related, so do not suggest me to use collection method. When partial is rendered inside the each loop, rails does not put li tags around it, but the anything except the partial gets li tags around them.
The question is not entirely clear to me, so maybe i should refrain from answering.
But, i would propose to use haml, which gives you much cleaner views.
Your main view would become:
%ul
= render :partial => "items/item", :collection => items
and your partial items\_item.html.haml would look like this
%li
%a
= item.title
I don't see a real link inside your li-item, so maybe you want something like:
%li
= link_to item.title, item_path(item)
Instead of this:
<% items.each do |item|%>
<%= render :partial => "items/item", :locals => { :title => item.title} %>
<% end %>
Try this:
<%= render :partial => "items/item", :collection => items %>

Resources