I am new to ruby on rails, and know this is a beginner error. I'm trying to create an application that displays user comments within a post. I have a comment partial pointing to my post#show, and when requesting to display <%= image_tag(comment.user.avatar.small.url, class: "media-object") if comment.user.avatar? %> from _comment.html.erb, I get "undefined method 'user' for nil:NilClass. I've read other questions on stackoverflow and I know this is saying it can't find a user associated with the post within comments and that I need to define user in my controller. I'm assuming it's for the post controller, but I can't seem to wrap my head around the specific syntax to display comments by user in post.
#comments_controller
def create
#topic = Topic.find(params[:topic_id])
#post = Post.find(params[:post_id])
#comment = current_user.comments.new(comment_params)
#comment.post = #post
#comment.save
authorize #comment
redirect_to [#topic, #post]
end
#posts_controller
def show
#topic = Topic.find(params[:topic_id])
#post = Post.find(params[:id])
end
#post model
class Post < ActiveRecord::Base
belongs_to :user
belongs_to :topic
has_one :summary
has_many :comments
mount_uploader :image, ImageUploader
#comment model
class Comment < ActiveRecord::Base
belongs_to :post
belongs_to :user
#rendering comment partial in post#show
<h1><%= #post.markdown_title %></h1>
<div class="row">
<div class="col-md-8">
<small>
<%= image_tag(#post.image.thumb.url) if #post.image? %></br>
<%= image_tag(#post.user.avatar.tiny.url) if #post.user.avatar? %>
submitted <%= time_ago_in_words(#post.created_at) %> ago by
<%= #post.user.name %>
</small>
<p><%= #post.markdown_body %></p>
</div>
<div class="col-md-4">
<% if policy(#post).edit? %>
<%= link_to "Edit", edit_topic_post_path(#topic, #post), class: 'btn btn-success' %>
<% end %>
<% if policy(#post).destroy? %>
<%= link_to "Delete Post", [#topic, #post], method: :delete, class: 'btn btn-danger', data: { confirm: "Are you sure you want to delete this post?" } %>
<% end %>
</div>
<div class="col-md-8">
<% if policy(Comment.new).create? %>
<%= render partial: 'comments/comment', locals: { comment: #comment } %>
<%= render partial: 'comments/form', locals: {topic: #topic, post: #post} %>
<% else %>
<h3>Sign up to Bloccit to post comments</h3>
<%= link_to "Sign up", new_user_registration_path, class: 'btn btn-primary' %></br></br>
<% end %>
#comment partial
<ul>
<% #post.comments.each do |c| %>
<div class="media">
<div class="media-left">
<%= image_tag(comment.user.avatar.small.url, class: "media-object") if comment.user.avatar? %>
</div>
<div class="media-body">
<small>
<%= comment.user.name %> commented <%= time_ago_in_words(comment.created_at) %> ago
<% if policy(comment).destroy? %>
| <%= link_to "Delete", [#topic, #post, comment], method: :delete %>
<% end %>
</small>
<li>
<p><%= c.body %></p>
</li>
</div>
<% end %>
Also, when I put #post.user = #user in posts_controller the error changes to "undefined method 'avatar' for nil:NilClass. Thanks for your help!
undefined method 'user' for nil:NilClass
The error is because of this line
<%= render partial: 'comments/comment', locals: { comment: #comment } %>
You don't have #comment initialized in show method of posts_controller and you are passing it as locals for comment, so comment.user returns error.
And also I've noticed that in _comment.html.erb you have this line <% #post.comments.each do |c| %>, so you can use c instead of comment.
<%= image_tag(c.user.avatar.small.url, class: "media-object") if c.user.avatar? %>
You are incorrectly referencing the block variable. Either change |c| to |comment| or change the comment references to c.
So like this:
<%= image_tag(c.user.avatar.small.url, class: "media-object") if c.user.avatar? %>
Related
My second-level comments, created using ActionText / Trix Editor are not displaying in my rails app. The original post displays fine as do first-level comments. But, comments on comments will not display.
I created an app using Rails 6 that has posts and nested comments. The posts and comments are working on the database side of things. However, after I installed ActionText so I could allow users to post and comment using rich-text only the post and first-level comments display their respective body content.
The posts and first-level comments render post.body and comment.body wrapped in a "trix-content" div. However, the output for the second-level, nested comment just doesn't seem to be grabbing the comment.body.
Comment Model Code:
class Comment < ApplicationRecord
has_rich_text :body
belongs_to :user
belongs_to :commentable, polymorphic: true
belongs_to :parent, optional: true, class_name: "Comment"
def comments
Comment.where(commentable: commentable, parent_id: id)
end
end
Comment Controller Code:
class CommentsController < ApplicationController
before_action :authenticate_user!
def create
#comment = #commentable.comments.new(comment_params)
#comment.user = current_user
if #comment.save
redirect_to #commentable, notice: "Comment was successfully created."
else
redirect_to #commentable, alert: "Something went wrong"
end
end
def destroy
#comment = #commentable.comments.find(params[:id])
#comment.destroy
redirect_to #commentable
end
private
def comment_params
params.require(:comment).permit(:body, :parent_id)
end
end
Comment Form Code:
<%= form_with model: [commentable, Comment.new], local: true, html: { class: local_assigns[:class], data: { target: local_assigns[:target]}} do |form| %>
<div class="form-group">
<%= form.rich_text_area :body, placeholder: "Add a comment", class: "form-control" %>
</div>
<div class="form-group">
<%= form.hidden_field :parent_id, value: local_assigns[:parent_id] %>
<%= form.submit class: "btn btn-primary" %>
</div>
<% end %>
Comment View/Show Page Code:
<% nesting = local_assigns.fetch(:nesting, 1) %>
<% max_nesting = local_assigns[:max_nesting] %>
<div <div class="col-lg-3">
</div>
<div class="col-lg-7">
<ul class="media-list media-list-stream mb-2">
<li class="media list-group-item mb-2">
<img class="mr-0">
<%= image_tag user_avatar(comment.user, 60), class: "rounded-circle" %>
</img>
<div class="media-body">
<div class="media-heading">
<small class="float-right text-muted"> <%= comment.created_at.strftime("%b %e, %Y") %> </small>
<h6 class="ml-3"><%= comment.user.name %></h6>
</div>
<div class="media ml-3">
<p><%= comment.body %></p>
</div>
<div class="media-footer ml-3">
<div data-controller="reply">
<small>
<%= link_to "Reply", "#", data: { action: "click->reply#toggle" } %>
<%= link_to "Delete", [comment.commentable, comment], method: :delete, data: {confirm: "Are you sure?"} if comment.user == current_user%>
</small>
<%= render partial: "comments/form", locals: {
commentable: comment.commentable,
parent_id: reply_to_comment_id(comment, nesting, max_nesting),
class: "d-none",
target: "reply.form"
} %>
</div>
</div>
</div>
</li>
<%= render comment.comments, nesting: nesting + 1, max_nesting: local_assigns[:max_nesting] %>
</ul>
</div>
I suspect the issue has something to do with the association of comments on comments because when I remove the nesting and comment on a comment I see the same result. It looks like someone posted a blank comment.
So, Chris Oliver from GoRails helped me solve this problem. I had to add an input ID for each Trix Editor form. Basically, Rails wasn't recognizing the second level comment forms correctly.
All I had to do was something like this:
<%= form.rich_text_area :body, placeholder: "Add a comment", class: "form-control", id: form.object.object_id %>
I'm working on ROR project and created the partial form for the comments but i get the error in show.html.rb "undefined method `comments'".I have tried to find what's long but no luck.The highlight of the error is on this image
Image
here is my _form.html.erb
<%= simple_form_for ([#message, #message.comments.build]) do |f| %>
<%= f.input :content , label: "Comments" %>
<%= f.button :submit, :class => "btn-custom" %>
<% end %>
class CommentsController < ApplicationController
def create
#message = Message.find(params[:message_id])
#comment = #message.comments.create(comment_params)
#comment.user_id = current_user.user_id
if #comment.save
redirect_to message_path(#message)
else
render 'new'
end
end
private
def comment_params
params.require(:comment).permit(:content)
end
end
And this is my show.html.rb
<div class="col-md-10 col-md-offset-1">
<div class="message-show">
<h2><%=#message.title %></h2>
<p class="message-posted-by"><%= time_ago_in_words(#message.created_at) %>
ago </p>
<p class="message-desc"><%= #message.description %></p>
<h3 class="comment-section-header">Discussion:</h3>
<p><%= render #message.comments %></p>
<h3 class="reply-to-msg">Reply</h3>
<%= render 'comments/form' %>
<div class="links btn-group">
<%= link_to "Back", root_path, class: "btn btn-default" %>
<%= link_to "Edit", edit_message_path, class: "btn btn-primary" %>
<%= link_to "Delete",message_path(#message), method: :delete,data: {confirm:"Are you sure?"} , class: "btn btn-danger" %>
</div>
</div>
</div>
Seems like your Message model is missing an association to comments:
# in app/models/message.rb
has_many :comments
How can I leave a comment and associate with the post in post' index page?
It is my PostsController:
def index
#posts = Post.all
"what should I add here?"
end
# GET /posts/1
# GET /posts/1.json
def show
#comments = #post.comments.all
#comment = #post.comments.build
end
and its my posts show view:
<p id="notice"><%= notice %></p>
<p>
<h3><%= #post.name %></h3>
</p>
<p>
<%= (#post.descriptopm).html_safe %>
</p>
<%= link_to 'Edit', edit_post_path(#post), :class => "btn btn-info btn-xs" %>
<%= link_to 'Back', posts_path, :class => "btn btn-info btn-xs" %>
<h3>Comments</h3>
<% #comments.each do |comment| %>
<div>
<strong><%= comment.user_name %></strong>
<br />
<p><%= (comment.body).html_safe %></p>
</div>
<% end %>
<%= render 'comments/form' %>
and its my posts index view:
<h1>Listing posts</h1>
<%= link_to 'Create a New Post', new_post_path, :class => "btn btn-success btn-sm" %>
<% #posts.each do |post| %>
<div class="post thumbnail">
<h3><%= post.name %></h3>
<div><%= (post.descriptopm).html_safe %></div>
<div class="bottom-bottoms">
<%= link_to 'Display', post, :class => "btn btn-info btn-xs" %>
<%= link_to 'Edit', edit_post_path(post), :class => "btn btn-info btn-xs" %>
<%= link_to 'Delete', post, method: :delete, data: { confirm: 'Are you sure?' }, :class => "btn btn-info btn-xs" %>
</div>
<h3>Comments</h3>
<% post.comments.each do |comment| %>
<div>
<strong><%= comment.user_name %></strong>
<br />
<p><%= (comment.body).html_safe %></p>
</div>
<% end %>
<%= render 'comments/form' %>
</div>
<% end %>
the post.rb :
class Post < ActiveRecord::Base
has_many :comments
end
the comment.rb :
class Comment < ActiveRecord::Base
belongs_to :post
end
the show page's comment function looks right
but when I leave a comment in my post's index page
It might not save the comment with the right post's id
how do I fix it?
and onother one:
how can I redirect the page to the index page after I save the comment not to comment's index page?
How can I leave a comment and associate with the post in post' index
page?
There are several things to consider:
Comment Objects
Firstly, you need to appreciate that since Rails is an object orientated framework (by virtue of being built on Ruby), you will need to ensure your new comment corresponds with the relevant Post object
I think this the core of what you're getting confused about:
def index
#posts = Post.all
# Here needs to go your "comment" build methodology. Except, it only works *per* post ;)
end
The trouble you have is that you can't "build" a comment for Posts - you have to build them per post, like this:
#post = Post.find 2
#post.comments.new #-> allows you to create a new comment for that particular post
--
Implementation
The best solution I can give you will be somewhat constricted, but will serve your purposes correctly. Here it is:
#app/controllers/posts_controller.rb
Class PostsController < ApplicationController
def index
#posts = Post.all
#comment = Comment.new
end
end
#app/controllers/comments_controller.rb
Class CommentsController < ApplicationController
def create
#comment = Comment.new(comment_params)
#comment.save
end
private
def comment_params
params.require(:comment).permit(:comment, :params, :post_id)
end
end
This will take into consideration that for each comment on your index, you'll have a form with the post_id attached; and will be a "linear" flow (IE you can only post a single comment at a time):
#app/views/posts/index.html.erb
<% #posts.each do |post| %>
<%= post.title %>
<%= form_for #comment do |f| %>
<%= f.hidden_field :post_id, value: post.id %>
<%= f.text_field :title %>
<%= f.submit %>
<% end %>
<% end %>
Yes, this will give each Post a "new comment" form on your index page (hence my denoting its constriction). However, what it will do is give you the ability to add a comment for any of the posts on the page, handling them with the CommentsController
To answer your second second question, you can use
redirect_to post_index_path
As for the first question, you can set the post id of the post by using:
#post.comments.new
Instead of just Comment.new, which won't set the ID of the post.
In this scenario...this is the best example that you can get help
Railscasts show post with comments using polymorphism
I'm working my way through the rails "Getting Started" tutorial, and I'm stuck at the point where they introduce partials. For some reason the partial doesn't work as described when rendered from new.html.erb, although it does work when rendered from edit.html.erb. When clicking "New" to get to the new.html.erb, I'm getting the following error:
Error:
"First argument in form cannot contain nil or be empty"
for the first line in the following partial:
_form.html.erb:
<%= form_for #post do |f| %>
<% if #post.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(#post.errors.count, "error") %> prohibited
this article from being saved:</h2>
<ul>
<% #post.errors.full_messages.each do |msg| %>
<li><%= msg %></li>
<% end %>
</ul>
</div>
<% end %>
<p>
<%= f.label :title %><br/>
<%= f.text_field :title %>
</p>
<p>
<%= f.label :text %><br/>
<%= f.textarea :text %>
</p>
<p>
<%= f.submit %>
</p>
<% end %>
new.html.erb:
<h1>New Post</h1>
<%= render 'form' %>
<%= link_to 'Back', posts_path %>
edit.html.erb:
<h1>Edit post</h1>
<%= render 'form' %>
<%= link_to 'Back', posts_path %>
posts_controller.rb:
...
def new
end
...
def edit
#post = Post.find(params[:id])
end
def update
#post = Post.find(params[:id])
if #post.update(post_params)
redirect_to #post
else
render 'edit'
end
end
...
It looks as though the new.html.erb doesn't know about the #post variable if it's a new post. The original new.html.erb looked like this:
<h1>New Post</h1>
<%= form_for :post, url: posts_path do |f| %>
...
... other than the use of symbols instead of #post, it's identical to the partial form.
Any ideas?
You don't set #post instance variable in your Posts#new action (and that's why it's nil). You should have:
def new
#post = Post.new
end
in your PostsController.
I am currently teaching myself some RoR and doing the tutorial, but adding some nicer layout and stuff with bootstrap and I am running into a problem which I cant figure out.
I am trying to do the validation part (http://guides.rubyonrails.org/getting_started.html#adding-some-validation), but when I use:
<% #post.errors.any? %>
I get this message:
undefined method `errors' for nil:NilClass
Extracted source (around line #9):
<legend><h1>Add Post</h1></legend>
<%= form_for :post, url: posts_path, html: {class: 'form-horizontal'} do |f| %>
<% if #post.errors.any? %>
<div id="errorExplanation">
Nothing works and I even copied and pasted the parts from the tutorial.
Here is the code for the view:
<p> </p>
<div class="span6"
<fieldset>
<legend><h1>Add Post</h1></legend>
<%= form_for :post, url: posts_path, html: {class: 'form-horizontal'} do |f| %>
<% if #post.errors.any? %>
<div id="errorExplanation">
<h2><%= pluralize(#post.errors.count, "error") %> prohibited this post from being saved:</h2>
<ul>
<% #post.errors.full_messages.each do |msg| %>
<li><%= msg %></li>
<% end %>
</ul>
</div>
<% end %>
<div class="control-group">
<%= f.label :title, :class => 'control-label' %>
<div class="controls">
<%= f.text_field :title, :class => 'span4' %>
</div>
</div>
<div class="control-group">
<%= f.label :content, :class => 'control-label' %>
<div class="controls">
<%= f.text_area :content, :rows => '7', :class => 'input-block-level' %>
</div>
</div>
<div class="form-actions">
<%= f.submit "Add Post", :class => 'btn btn-success' %>
<%= link_to "Cancel", posts_path, :class => 'btn', :style => 'float:right;' %>
</div>
<% end %>
</fieldset>
</div>
And my posts_controller:
class PostsController < ApplicationController
def new
end
def create
#post = Post.new(params[:post].permit(:title, :content))
if #post.save
redirect_to #post
else
render 'new'
end
end
def show
#post = Post.find(params[:id])
end
def index
#posts = Post.order("created_at desc")
end
private
def post_params
params.require(:post).permit(:title, :content)
end
end
What am I missing? Thanks in advance!
You need to define #post in your new action too.
def new
#post = Post.new
end
You're getting the NilClass error because #post has no value (it's nil) when you first load the form on the new action.
When you do the render :new in your create action there is no problem because it's using the #post you've defined at the top of create.
Update the create method in posts.controller.rb file with the below piece of code. It worked for me.
def create
#post = Post.new(params[:post].permit(:title, :text))
#post.save
redirect_to #post
end
In your posts_controller, add this :
def new
#post = Post.new
end
If you have made the changes to edit/create etc. and it still gives you ActionView::Template::Error (undefined method `errors' for nil:NilClass):
Try restarting rails server
I kept receiving the error even after correcting the files in articles_controller.rb
and still got the error. It was fixed after i restarted rails server.