Jim Hogan
Jim Hogan

Reputation: 194

rails 4 undefined local variable rendering partial has_many_through

I'm trying to display all of the comments on all of a user's posts in a has_many_through association.

routes.rb:

resources :posts do
  resources :comments
end

models:

class User < ActiveRecord::Base
  has_many :posts, dependent: :destroy
  has_many :comments, :through => :posts, dependent: :destroy
end

class Post < ActiveRecord::Base  
  belongs_to :user
  has_many :comments, dependent: :destroy
end

class Comment < ActiveRecord::Base
  belongs_to :user
  belongs_to :post
end

users_controller.rb

def activity
  @user = current_user
  @posts = @user.posts
  @comments = @user.comments
end

activity.html.erb

<h4>My Activity</h4>
<ol class="posts">
  <%= render @comments.last(3) %>
</ol>

_comment.html.erb

<div class="comment" id="comment-<%= post.id %>-<%= comment.id %>">
    <%= link_to smavatar_for(comment.user), user_path(comment.user.name) %>
    <span class="user"><%= link_to comment.user.name, user_path(comment.user.name) %></span>
  <div class="comment-content">
    <%= simple_format(comment.content) %>
  </div>
</div>

The line <%= render @comments.last(3) %> in activity.html.erb gives the error

undefined local variable or method `post' for Class

for the line <div class="comment" id="comment-<%= post.id %>-<%= comment.id %>"> in _comment.html.erb. I know I probably need to pass local variables to the partial, I just can't figure out how to do it. <%= @comments.last(3) %> instead of <%= render @comments.last(3) %> prints all the comment params to the view, so it's recognizing the collection. Add locals: { comment: @comment, post: @post } still gets me undefined local variable post for Class, and post: @comment.post gets a nilClass error. I've been all over SO and the RoR Guides backwards and forwards on rendering partials, and I'm still very unclear on what to pass when, so any help there in general is appreciated.

Upvotes: 0

Views: 370

Answers (2)

alxibra
alxibra

Reputation: 727

Different perspective you just need to change post.id to comment.post.id in _comment.html.erb without pass locals, it more clearner and simple

<div class="comment" id="comment-<%= comment.post.id %>-<%= comment.id %>">
  <%= link_to smavatar_for(comment.user), user_path(comment.user.name) %>
  <span class="user"><%= link_to comment.user.name, user_path(comment.user.name) %></span>
  <div class="comment-content">
    <%= simple_format(comment.content) %>
  </div>
</div>

does it work?

Upvotes: 0

Carlos Ramirez III
Carlos Ramirez III

Reputation: 7434

Reference the Post from the Comment itself

Assuming every Comment has a belongs_to :post association

Call the partial using the shortcut syntax exactly as you had

<%= render @comments.last(3) %>

Inside the partial, use comment.post or comment.post_id to get Post data

E.g.

<div class="comment" id="comment-<%= comment.post_id %>-<%= comment.id %>">
    <%= link_to smavatar_for(comment.user), user_path(comment.user.name) %>
    <span class="user"><%= link_to comment.user.name, user_path(comment.user.name) %></span>
  <div class="comment-content">
    <%= simple_format(comment.content) %>
  </div>
</div>

No need to use locals in this scenario

Notice that you tried to pass @post in through the locals option, however in your controller you have a @posts variable (plural), not @post singular.

Because each of the Comment records may belong to a different Post, there is no single option you can pass for Post.

The only other way to use locals is if you broke out the partial into a loop and rendered each Comment individually, e.g.

<% @comments.last(3).each do |comment| %>
   <%= render comment, post: comment.post %>
   <!-- OR -->
   <%= render partial: "comments/comment", locals: { comment: comment, post: comment.post } %>
<% end %>

Notice we still need to call comment.post to get the Post associated with the Comment. Because of this, it's much simpler to just use the shortcut syntax and reference the Post from the Comment within the partial.

Upvotes: 2

Related Questions