I'm new to rails. Appreciate any help.
I'm trying to extend functionality of the Blog, described in the official Rails 7 guide with comments validation.
Here is my github repo link for the project code.
What is the right way to add validation to the comments and show error messages on the frontend after submitting invalid form?
Setup
ubuntu - 22.04
rvm - 1.29.12
ruby - 3.1.4p223
rails - 7.0.4.3
Currently if I add these validations on the Comment model validation doesn't work. When I submit an empty comment form it just redirects me to the post page.
/app/models/comment.rb
class Comment < ApplicationRecord
belongs_to :post
validates :author, presence: true
validates :body, presence: true
end
Also tried to handle comment saving in the comments_controller
. But it saves the comment with an empty :author
and :body
fields.
/app/controllers/comments_controller.rb
def create
@post = Post.find(params[:post_id])
@comment = @post.comments.build(comment_params)
if @comment.save
redirect_to @post
else
render @post, status: :unprocessable_entity
end
end
/app/views/comments/_form.html.erb
<%= form_with model: [@post, @post.comments.build] do |form| %>
<div class="mb-3">
<%= form.label :author, class: 'form-label' %><br>
<%= form.text_field :author, class: 'form-control', placeholder: 'John Doe' %>
</div>
<div class="mb-3">
<%= form.label :body, class: 'form-label' %><br>
<%= form.text_area :body, class: 'form-control', rows: 3 %>
</div>
<div class="mb-3">
<%= form.submit 'Add comment', class: 'btn btn-outline-primary' %>
</div>
<% end %>
Here are some visualization that i want to achieve(I've just add some html through dev tools). First screenshot - post page before comment form submission, just clean page. Second screenshot(what I want to ahive) - the same page after i submit comment form with empty Author and Body fields.
Second screenshot(this is what I want comments form to be like)
Make these two changes to your CommentsController class create
method when the save fails:
@post = Post.find(params[:post_id])
render '/posts/show', status: :unprocessable_entity
The reason we need to re-fetch the post is that when the post's show view is re-rendered (in /posts/show.html.erb
), the <%= render @post.comments %>
line in the show view will list out all the comments for the post - which will include the new "failing" comment unless we do the re-fetch before rendering on fail.
So that's the first part of the solution. The next is displaying the error message above the comment form and re-populating the comment form's author and body with their original values. To make that happen, we need to do the following in /posts/show.html.erb
First, to render the comment form (in your <h4>Add a comment</h4>
section), do the following:
<%= render partial: 'comments/form', locals: { comment: @comment } %>
This passes the comment down to the comment partial view (/comments/_form.html.erb
) for use. Here's a sample of that partial view that will accomplish the goals:
<p style="color: red"><%= comment ? comment.errors.full_messages.to_sentence : nil %></p>
<%= form_with model: [ @post, @post.comments.build ] do |form| %>
<p>
<%= form.label :author %><br>
<%= text_field_tag 'comment[author]', comment&.author %>
</p>
<p>
<%= form.label :body %><br>
<%= text_area_tag 'comment[body]', comment&.body %>
</p>
<p>
<%= form.submit %>
</p>
<% end %>
The Safe Navigation operator (&.) checks to see if the object is nil. So, if there is a valid comment for the form (which will only be the case on failed re-render) then display the original data, otherwise, the fields will be empty. You could similarly use the &.
syntax on the error message, but you'd need to add it to each property - so I left the "long" version instead.
Now, when any of your comment model validations fail, an error message will be displayed and the original contents of your failing comment will be re-populated in their respective fields.
As a final "clean-up", you could consider passing a local post
to the /comments/_form.html.erb
rather than accessing @post
directly in the partial.