comments validation errors not shown in posts view - ruby-on-rails

I am working on basic blog engine and i have applied validations on comments but when i do a submit it doesn't show errors, instead it shows ActiveRecord::RecordInvalid by rails which is default.
my comments controller is
def create
#post = Post.find(params[:post_id])
#comment = #post.comments.create!(params[:comment])
redirect_to #post
end
my posts/show view is as below which is working fine for commenting
<%= form_for [#post, Comment.new] do |f| %>
<p class="comment-notes">Your email address will not be published. Required fields are marked <span class="required">*</span></p>
<p>
<b><%= f.label :name, "Name * " %></b><%= f.text_field :name %><br /></p>
<p>
<b><%= f.label :body, "Comment" %></b><%= f.text_area :comment, :cols => 60, :rows => 5 %>
</p>
<p>
<%= f.submit "Post Comment" %>
</p>
can anybody help me to show validation errors on the same posts/show view?
thanks in advance

replace
#comment = #post.comments.create!(params[:comment])
redirect_to #post
with
#comment = #post.comments.create(params[:comment])
if #comment.errors.any?
render "posts/show"
else
redirect_to #post
end
unlike create, create! will raise error if validations fail
in posts/show
<%= form_for [#post, Comment.new] do |f| %>
<% if #comment && #comment.errors.any? %>
<% #comment.errors.full_messages.each do |msg| %>
<li><%= msg %></li>
<% end %>
<% end %>
...

Try this:
def create
#post = Post.find(params[:post_id])
#comment = #post.comments.new(params[:comment])
if #post.save
redirect_to #post
else
flash[:error] = "Correct errors"
end
end
In Post model:
accepts_nested_attributes_for :comments
or
If you don't want to make as nested model:
def create
#post = Post.find(params[:post_id])
#comment = #post.comments.new(params[:comment])
if #comment.save
redirect_to #post
else
flash[:error] = "Correct errors"
end
end

Related

how to edit comment within article's page - keep getting error about nil

I have typical article and comment blog format. It's a typical has_many/belongs_to, ie, the Guide's blog type.
However, I'm trying to edit the comment within the article, and I'm clueless about building the right form for this.
It's also in a partial which makes it more complicated for me.
Any help and/or educating me would be appreciated.
Comment's model
class Comment < ApplicationRecord
belongs_to :article
end
Article's model
class Article < ApplicationRecord
has_many :comments
validates :title, presence: true, length: { minimum: 5}
end
Article's Show page
<p>
<strong>Title:</strong>
<%= #article.title %>
</p>
<p>
<strong>Text:</strong>
<%= #article.text %>
</p>
<h2>Comments</h2>
<%= render #article.comments %>
<h2>Add a comment:</h2>
<%= render 'comments/form' %>
<%= link_to 'Edit', edit_article_path(#article) %> |
<%= link_to 'Back', articles_path %>
Comment's _comment.html.erb page
<p>
<strong>Commenter:</strong>
<%= comment.commenter %>
</p>
<p>
<strong>Comment:</strong>
<%= comment.body %>
</p>
<p>
<%= link_to 'Destroy Comment', [comment.article, comment],
method: :delete,
data: { confirm: 'Are you sure?' } %>
</p>
<p>
<%= link_to 'Edit', edit_article_comment_path(#article, comment) %>
</p>
Comment's _form.html
<%= form_for([#article, #comment]) do |f| %>
<p>
<%= f.label :commenter %><br>
<%= f.text_field :commenter %>
</p>
<p>
<%= f.label :body %><br>
<%= f.text_area :body %>
</p>
<p>
<%= f.submit %>
</p>
<% end %>
Error
It comes from Comment's _form.html.erb page in reference to this line: <%= form_for([#article, #comment]) do |f| %> ... and the error is: First argument in form cannot contain nil or be empty ...
Articles Controller
class ArticlesController < ApplicationController
def index
#articles = Article.all
end
def new
#article = Article.new
end
def edit
#article = Article.find(params[:id])
end
def create
#render plain: params[:article].inspect
##article = Article.new(params[:article])
##article = Article.new(params.require(:article).permit(:title, :text))
#article = Article.new(article_params)
if #article.save
redirect_to #article
else
render 'new'
end
end
def update
#article = Article.find(params[:id])
if #article.update(article_params)
redirect_to #article
else
render 'edit'
end
end
def show
#article = Article.find(params[:id])
end
def destroy
#article = Article.find(params[:id])
#article.destroy
redirect_to articles_path
end
private
def article_params
params.require(:article).permit(:title, :text)
end
end
Comments Controller
class CommentsController < ApplicationController
def create
#article = Article.find(params[:article_id])
#comment = #article.comments.create(comment_params)
redirect_to article_path(#article)
end
def destroy
#article = Article.find(params[:article_id])
#comment = #article.comments.find(params[:id])
#comment.destroy
redirect_to article_path(#article)
end
private
def comment_params
params.require(:comment).permit(:commenter, :body)
end
end
First argument in form cannot contain nil or be empty
The error is due to #article and #comment being nil in the form. Since you are rendering the form with a partial, you need send variables to the partial as well
Change <%= render 'comments/form' %> to <%= render 'comments/form', article: #article, comment: #comment %>
and in the form have this <%= form_for([article, comment]) do |f| %>
Also in the show method of articles controller, you should define #comment
def show
#article = Article.find(params[:id])
#comment = Comment.new
end

Rails: Can't render error messages

I'm trying to render errors messages that arise from model validation but I end up with this error:
First argument in form cannot contain nil or be empty
Hopefully the provided code with be of assistance... I was able to at least get the pages to load when I used :comment instead of #comment, but then I didn't know how to then render the errors partial because it was a symbol and not an actual object. Now that I have changed it to an instance variable of an object, I get the error.
Error messages partial
<% if object.errors.any? %>
<div id="error_explanation">
<div class="alert alert-danger">
The form contains <%= pluralize(object.errors.count, "error") %>
</div>
<ul>
<% object.errors.full_messages.each do |msg| %>
<li><%= msg %></li>
<% end %>
</ul>
</div>
<% end %>
Comment form
<%= form_for #comment, url: comments_path do |f| %>
<%= render 'shared/error_messages', object: f.object %>
<%= f.hidden_field :user_id, value: current_user.id %>
<%= f.hidden_field :post_id, value: post.id %>
<%= f.text_area :content, size: "60x2", placeholder: "Comment on this post..." %>
<%= f.submit "Comment" %>
Post form
<%= form_for [#user, #post] do |f| %>
<%= render 'shared/error_messages', object: f.object %>
<%= f.text_area :content, size: "60x12", placeholder: "What do you want to say?" %>
<%= f.submit "Post" %>
<% end %>
class CommentsController < ApplicationController
def index
#comments = Comment.all
end
def new
#comment = Comment.new
#user = User.find(params[:user_id])
end
def create
#user = current_user
##post = Post.find(params[:post_id])
#comment = #user.comments.build(comment_params)
if #comment.save
flash[:success] = "Comment Posted!"
redirect_back(fallback_location: root_path)
else
flash[:notice] = "Could not post comment"
redirect_back(fallback_location: root_path)
end
end
private
def comment_params
params.require(:comment).permit(:content, :user_id, :post_id)
end
end
class PostsController < ApplicationController
def index
#posts = Post.all
#user = User.find(params[:user_id])
end
def new
#post = Post.new
#user = User.find(params[:user_id])
end
def create
#post = current_user.posts.build(post_params)
if #post.save
flash[:success] = "Posted!"
redirect_to user_path(current_user)
else
flash[:notice] = "Post could not be submitted"
redirect_to users_path
end
end
private
def post_params
params.require(:post).permit(:content)
end
end
I was able to get it by declaring an #comment variable in my posts#index action because that is where the view is being rendered from

Trying to to add a nested post with the ancestry gem

I have topics and posts and they both have relationships. I've been following the guide on how to add a nested post but when i try to reply to a post i get Couldn't find Topic with 'id'=.
In my Posts controller
def create
#topic = Topic.find params[:topic_id]
#post = Post.new(post_params)
#post.user_id = current_user.id
#post.topic_id = #topic.id
if #post.save
redirect_to #topic
else
render :new
end
end
In my Topics controller
def show
#topic = Topic.find params[:id]
#post = Post.new(:parent_id => params[:parent_id])
#posts = #topic.posts
#topic.punch(request)
end
I'll keep it short for how i have my reply button in my topics/show.html.erb page
<% #posts.each do |post| %>
<%= link_to "Reply", new_topic_post_path(#topic, :parent_id => post) %>
<% end %>
Now this is my form
<%= simple_form_for [#topic, #post] do |f| %>
<%= f.hidden_field :parent_id %>
<%= f.input :content, as: :pagedown, input_html: { preview: true, rows: 10 }, label: 'Markdown' %>
<%= f.submit "Post", class: 'button expanded' %>
<% end %>
#Kristján Answered my question. I was missing params[:topic_id]

Adding Comments to multiple models

Im trying to add comments to my topics model the same way you can add comments to posts on my app. i currently have to partials for comments _comment.html.erb and _form.html.erb
_comment :
<%= content_tag :div, class: 'media', id: "comment-#{comment.id}" do %>
<div class= "media">
<div class= "media-body">
<small>
<%= comment.user.name %> commented <%= time_ago_in_words(comment.created_at) %> ago
<% if user_is_authorized_for_comment?(comment) %>
| <%= link_to "Delete", [comment.post, comment], method: :delete %>
<% end %>
</small>
<p> <%= comment.body %></p>
</div>
</div>
<% end %>
_form :
<h4>Add a comment</h4>
<%= form_for [post, comment] do |f| %>
<div class="form-group">
<%= f.label :body, class: 'sr-only' %>
<%= f.text_field :body, class: 'form-control', placeholder: "Enter a new comment" %>
</div>
<%= f.submit "Submit Comment", class: 'btn btn-default pull-right' %>
<% end %>
my topic show is :
#DISPLAY Topic comments here
<h3> Comments</h3>
<%= render #topic.comments %>
</div>
<% if current_user %>
<%= render 'comments/form', comment: Comment.new, post: #post %>
<% end %>
#------
comment controller :
def create
#post = Post.find(params[:post_id])
comment = #post.comments.new(comment_params)
comment.user = current_user
if comment.save
flash[:notice] = "Comment saved successfully."
redirect_to [#post.topic, #post]
else
flash[:alert] = "Comment failed to save."
redirect_to [#post.topic, #post]
end
end
def destroy
#post = Post.find(params[:post_id])
comment = #post.comments.find(params[:id])
if comment.destroy
flash[:notice] = "Comment was deleted"
redirect_to [#post.topic, #post]
end
end
i have updated the routes for topic comments :
resources :topics do
resources :posts, except: [:index]
resources :comments, only: [:create, :destroy]
end
my question is do i need to create a separate partial to add comments to topics or can i update my _comment partial to work for both post and topic comments . and how can i accomplish this ?
Models
You'll need a polymorphic association on the Comment model:
#app/models/comment.rb
class Comment < ActiveRecord::Base
belongs_to :commentable, polymorphic: true
end
#app/models/topic.rb
class Topic < ActiveRecord::Base
has_many :comments, as: :commentable
end
#app/models/post.rb
class Post < ActiveRecord::Base
has_many :comments, as: :commentable
end
Controllers
This will allow you to save the comments for your various models, the controllers / flow coming secondary:
#config/routes.rb
resources :topics, :posts do
resources :comments, only: [:create, :destroy] #-> url.com/topics/:topic_id/comments
end
#app/controllers/comments_controller.rb
class CommentsController < ApplicationController
def create
id = params[:post_id] || params[:topic_id]
if params[:post_id]
#parent = Post.find id
elsif params[:topic_id]
#parent = Topic.find id
end
#comment = #parent.comments.find params[:id]
#comment.save
end
def destroy
#parent = params[:post_id] || params[:topic_id]
#comment = #parent.comments.new comment_params
#comment.destroy
end
private
def comment_params
params.require(:comment).permit(:x, :y)
end
end
Because you're passing the data to the comments controller, you'll only need to evaluate which #parent you're working with.
Views
For your views, you need to pass locals to your _form partial:
#app/views/posts/show.html.erb
<%= render "comments/form", locals: {parent: #post} %>
#app/views/comments/_form.html.erb
<%= form_for [parent, parent.comments.new] do |f| %>
I had this road-block as well and here is what I came up with that passed. I must first give #Richard Peck applause for getting my wheels turning, so thank you :).
Models
Did not implement a polymorphic association. Stuck with has_many and belongs_to, nothing more
Partials
_comment.html.erb
Set up the delete partial to accept "parent" as a local
<div class="media">
<div class="media-body">
<small>
<%= comment.user.name %>
commented
<%= time_ago_in_words(comment.created_at) %>
ago
<% if user_is_authorized_for_comment_via_post?(comment) %>
|
<%= link_to "Delete", [parent, comment], method: :delete %>
<% end %>
</small>
<p>
<%= comment.body %>
</p>
</div>
</div>
_form.html.erb
same idea as _comment.html.erb, see above
<h4>Add a comment</h4>
<%= form_for [parent, comment] do |f| %>
<div class="form-group">
<%= f.label :body, class: 'sr-only' %>
<%= f.text_field :body, class: 'form-control', placeholder: "Enter a new comment" %>
</div>
<%= f.submit "Submit Comment", class: 'btn btn-default pull-right' %>
<% end %>
Setting up CommentController
...
def create
# upon clicking on create, determine what param id is passed
if params[:post_id]
# if it is a post id, set instance of post id as #parent
#parent = Post.find(params[:post_id])
elsif params[:topic_id]
# if it is a topic id, set instance of topic id as #parent
#parent = Topic.find(params[:topic_id])
end
# create instance as #comment. Build/create
# comment belonging to #parent (Topic or Post)
#comment = #parent.comments.build(comment_params)
# The comment must be associated to the current user.
# A comment must have a user, and value of user within instance of #comment
# is currently nil. Set user id as current user
#comment.user = current_user
# save comment to database
if #comment.save
# direction of save through if and elsif
# Redirection depends on the comment's parent.
# .is_a? method determines if it is of a certain class. Here, is #parent
# of class Post? Is #parents is the same parent id passed through params?
if #parent.is_a?(Post) # template error with this included: (== params[:post_id])
flash[:notice] = 'Comment saved successfully'
redirect_to [#parent.topic, #parent]
# if not part of the class Post, is it a Topic? If so, save here and
# redirect to the topic after save
elsif #parent.is_a?(Topic)
flash[:notice] = 'Comment saved successfully'
redirect_to #parent
end
end
end
def destroy
comment = Comment.find(params[:id])
# #topic = Topic.find(params[:topic_id])
# topic_comment = #topic.comments.find(params[:id])
# #post = Post.find(params[:post_id])
# post_comment = #post.comments.find(params[:id])
if comment.destroy
flash[:notice] = 'Comment was deleted'
redirect_to :back
else
flash[:alert] = "Comment counld't be deleted. Try again"
redirect_to :back
end
end
...
Passing in Comments from topic/show and post/show
topic/show
Note: notice how locals are passed into the controller from here
...
<div class="row">
<%= render 'comments/form', comment: Comment.new, parent: #topic %>
</div>
<% #topic.comments.each do |comment| %>
<%= render partial: 'comments/comment', locals: { parent: #topic, comment: comment } %>
<% end %>
...
post/show
<% if current_user %>
<% #post.comments.each do |comment| %>
<%= render partial: 'comments/comment', locals: { parent: #post, comment: comment } %>
<% end %>
<% end %>
<% if current_user %>
<%= render 'comments/form', comment: Comment.new, parent: #post %>
<% end %>
Hope this helps.

Show user's email when commenting rather than asking for input

I created a blog in which users can create comments under posts. Registered users, Admins and Guests will be able to post comments.
So in the _form.html.erb I wrote this:
<%= simple_form_for([#post, #post.comments.build], html: {class: 'form-horizontal' }) do |f| %>
<% if current_user || current_admin %>
<% #comment.commenter = current_user.try(:email) || current_admin.try(:email) %>
<% else %>
<%= f.input :commenter %>
<% end %>
<%= f.input :body %>
<%= f.button :submit %>
<% end %>
However I get this error: undefined local variable or method `comment'.
When I try to change #comment.commenter to #post.comments I get the error: undefined method `each' for "example#person.com":String.
Is there a way set the commenter's name if its registered. Like in the controller maybe?
The controller code is:
class CommentsController < ApplicationController
def create
#post = Post.find(params[:post_id])
#comment = #post.comments.create(comments_params)
redirect_to post_path(#post)
end
def destroy
#post = Post.find(params[:post_id])
#comment = #post.comments.find(params[:id])
#comment.destroy
redirect_to post_path(#post)
end
private
def comments_params
params.require(:comment).permit(:commenter, :body)
end
end
If you need any other information please let me know.
Thanks
<%= simple_form_for(#comment),url: [#post,#comment], html: {class: 'form-horizontal' }) do |f| %>
<%= f.input :commenter, value: comment_by_user %>
<%= f.input :body %>
<%= f.button :submit %>
Helper
def comment_by_user
current_user.try(:email) || current_admin.try(:email) if current_user || current_admin
end
Controller
class CommentsController < ApplicationController
before_filter :find_post
def new
#comment = #post.comments.build
end
def create
#comment = #post.comments.create(comments_params)
redirect_to post_path(#post)
end
def destroy
#comment = #post.comments.find(params[:id])
#comment.destroy
redirect_to post_path(#post)
end
private
def find_post
#post = Post.find(params[:post_id])
end
end
Found a solution:
Controller:
def create
#post = Post.find(params[:post_id])
#comment = #post.comments.create(comments_params)
if current_admin || current_user
#comment.commenter = current_user.try(:email) || current_admin.try(:email)
#comment.save
end
redirect_to post_path(#post)
end
View:
<%= simple_form_for([#post, #post.comments.build], html: {class: 'form-horizontal' }) do |f| %>
<% if current_admin || current_user %>
<%= f.input :body %>
<% else %>
<%= f.input :commenter %>
<%= f.input :body %>
<% end %>
<%= f.button :submit %>
<% end %>
Thanks for the help.

Resources