Rendering a partial in rails. Specifying the partial for a resource gives an error, but not specifying a partial works fine. What gives?

StackOverflow https://stackoverflow.com/questions/21381259

Question

I've got this working now quite accidentally, but I don't understand what causes it to break when I explicitly specify what partials are to be used for rendering the resource/s. Can anyone explain it?

The index template for my Posts controller contained the following line, which was giving me an error:

<%= render partial: 'posts', collection: @posts %>

The error (in my browser) said:

NoMethodError in Posts#index

Showing /Users/applebum/Sites/rails_projects/eventful2/app/views/posts/_posts.html.erb where line #1 raised:

undefined method `any?' for #<Post:0x000001064b21f0>
Extracted source (around line #1):

1: <% if posts.any? %>
2:  <div id="posts">
3:      <% posts.each do |post| %>
4:          <%= render partial: "posts/post", locals: { post: post } %>

Changing the problem line to

<%= render @posts %>

made the error disappear and the posts appear (displayed nicely in markup from the appropriate partials) as I had wanted and expected them to.

Here's my _posts.html.erb partial:

<% if posts.any? %>
    <div id="posts">
        <% posts.each do |post| %>
            <%= render partial: "posts/post", locals: { post: post } %>
            <% # render :partial => "comments/comments", :collection => post.comments %>
        <% end %>
    </div>
<% end %>

And the _post.html.erb partial it's referring to, if that matters:

<div class="post" id="post_<%= "#{post.id}" %>">

    <div class="post_inner">
        <%= link_to avatar_for(post.user, size: "small"), post.user.profile %>

        <div class="post_body">

            <div class="user-tools">
                <% if can? :destroy, post %>
                    <%= link_to '<i class="fi-x"></i>'.html_safe, post, :method => :delete, remote: true, :class => "delete", :confirm => "Are you sure you want to delete this post?", :title => post.content %>
                <% end %>
            </div>

            <h5 class="username">
                <%= link_to post.user.name, post.user.profile %> 
                <span class="timestamp">&bull; <%= time_ago_in_words(post.created_at) %> ago</span>
            </h5>

            <div class="content">
                <%= post.content %>
            </div>

            <ul class="foot">       
                <li>Like<li>
                <li>Share</li>
            </ul>

        </div>
    </div>

</div>

And the relevant bits from the controller:

class PostsController < ApplicationController

    respond_to :html, :js # Allow for AJAX requests as well as HTML ones.

    before_filter :load_postable

    load_and_authorize_resource

    def index
        @post = Post.new
        @posts = @postable.posts
    end

    private #################

    def load_postable
        klass = [User, Event].detect { |c| params["#{c.name.underscore}_id"] } # Look for which one of these there's a ***_id parameter name for
        @postable = klass.find(params["#{klass.name.underscore}_id"]) # Call find on that, passing in that parameter. eg Event.find(1)
    end

Can anyone explain to me what's going on here? I couldn't find anything in the Layouts and Rendering guide at rubyonrails.org.

Thanks!

Was it helpful?

Solution

Your error comes from assuming :collection and @posts mean the same thing when rendering. From Rails Docs (point 3.4.5):

Partials are very useful in rendering collections. When you pass a collection to a partial via the :collection option, the partial will be inserted once for each member in the collection

So, if you use that, for each post, you will be doing post.any? which fails as any? isn't defined for a single post.

From the same docs, you should check if render returns Nil to see if the collection is empty:

<h1>Posts</h1>
<%= render(@posts) || "There are no posts." %>

PD: Use the partial to render only one post, not all of them.

GL & HF.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top