Working through Michael Hartl's Rails Tutorial 2nd Ed Chapter 10 Exercise 5, I've run into problem with partials and collections and using a partial within a partial.
Chapter 10, Exercise 5 states: "Using partials, eliminate the duplication in the delete links from Listing 10.46 and Listing 10.47."
Listing 10.46 : app/views/microposts/_micropost.html.erb
<li>
<span class="content"><%= micropost.content %></span>
<span class="timestamp">
Posted <%= time_ago_in_words(micropost.created_at) %> ago.
</span>
<% if current_user?(micropost.user) %>
<%= link_to "delete", micropost, method: :delete,
confirm: "You sure?",
title: micropost.content %>
<% end %>
</li>
Listing 10.47 : app/views/shared/_feed_item.html.erb
<li id="<%= feed_item.id %>">
<%= link_to gravatar_for(feed_item.user), feed_item.user %>
<span class="user">
<%= link_to feed_item.user.name, feed_item.user %>
</span>
<span class="content"><%= feed_item.content %></span>
<span class="timestamp">
Posted <%= time_ago_in_words(feed_item.created_at) %> ago.
</span>
<% if current_user?(feed_item.user) %>
<%= link_to "delete", feed_item, method: :delete,
confirm: "You sure?",
title: feed_item.content %>
<% end %>
</li>
My approach was to create this file shared/_item_delete_link.html.erb
<%= link_to "delete", item, method: :delete,
confirm: "You sure?",
title: item.content %>
Then use this partial in the original partials like this:
Listing 10.46 : app/views/microposts/_micropost.html.erb
<li>
<span class="content"><%= micropost.content %></span>
<span class="timestamp">
Posted <%= time_ago_in_words(micropost.created_at) %> ago.
</span>
<% if current_user?(micropost.user) %>
<%= render partial: 'shared/item_delete_link', collection: @microposts, as: :item %>
<% end %>
</li>
Listing 10.47 : app/views/shared/_feed_item.html.erb
<li id="<%= feed_item.id %>">
<%= link_to gravatar_for(feed_item.user), feed_item.user %>
<span class="user">
<%= link_to feed_item.user.name, feed_item.user %>
</span>
<span class="content"><%= feed_item.content %></span>
<span class="timestamp">
Posted <%= time_ago_in_words(feed_item.created_at) %> ago.
</span>
<% if current_user?(feed_item.user) %>
<%= render partial: 'shared/item_delete_link', collection: @feed_items, as: :item %>
<% end %>
</li>
This got all my tests to pass, so I thought it was working until I checked it in the browser: http://grab.by/daUk
The entire collection is being rendered again for each of the _item_delete_link partials, whereas what I wanted was to pass through the local variable from the original collection used in the parent partial.
I tried using the locals: { }
and the object:
options for render, but had no luck.
Anyone know the answer? Thanks!
Figured it out. You want to pass the locals through to the nested partial, not re-render the collection. To do this, must use the locals: { symbol: :original-local }
option (written using the new Ruby hash syntax).
So, the answer should be this:
The partial ... app/views/shared/_item_delete_link.html.erb
<%= link_to "delete", item, method: :delete, confirm: "You sure?",
title: item.content %>
Listing 10.46 : app/views/microposts/_micropost.html.erb
<li>
<span class="content"><%= micropost.content %></span>
<span class="timestamp">
Posted <%= time_ago_in_words(micropost.created_at) %> ago.
</span>
<% if current_user?(micropost.user) %>
<%= render partial: 'shared/item_delete_link', locals: { item: micropost } %>
<% end %>
</li>
Listing 10.47 : app/views/shared/_feed_item.html.erb
<li id="<%= feed_item.id %>">
<%= link_to gravatar_for(feed_item.user), feed_item.user %>
<span class="user">
<%= link_to feed_item.user.name, feed_item.user %>
</span>
<span class="content"><%= feed_item.content %></span>
<span class="timestamp">
Posted <%= time_ago_in_words(feed_item.created_at) %> ago.
</span>
<% if current_user?(feed_item.user) %>
<%= render partial: 'shared/item_delete_link', locals: { item: feed_item } %>
<% end %>
</li>
ADDING TO THE PARTIAL: It's possible to refactor this a bit and also include the user conditional into the partial. So, you can make the partial into:
app/views/shared/_item_delete_link.html.erb
<% if current_user?(item.user) %>
<%= link_to "delete", item, method: :delete,
confirm: "You sure?", title: item.content %>
<% end %>
Then the calls to render must pass in user as well. So, Listing 10.46 : app/views/microposts/_micropost.html.erb
<%= render partial: "shared/item_delete_link",
locals: { item: micropost, user: @user } %>
Listing 10.47 : app/views/shared/_feed_item.html.erb
<%= render partial: "shared/item_delete_link",
locals: { item: feed_item, user: @user } %>
ANOTHER QUESTION:
I am confused about when to pass in the object, versus a local variable.
In this case, I had to pass in the local variables micropost
and feed_item
, but the instance variable @user
.
Anyone know why this is true in this case?