I'm a beginner with "super" who is struggling with Rails every day. Share for organizing your own knowledge.
The other day, I learned how to implement asynchronous communication in Rails, but I haven't fully digested it, so I'll output it to deepen my understanding! Assuming a site that posts user-recommended books, you can comment on the posted books.
This time, when you press the send button with the comment field blank, the error message will appear directly above the comment field, and the error message itself will be implemented by asynchronous communication. Let's go fast!
models/comment.rb
class BookComment < ApplicationRecord
belongs_to :user #Link to User model
belongs_to :book #Linked to Book model
validates :body, presence: true, length: {maximum: 150}
end
By setting presence: true
in the comment model, posting in the blank is prohibited.
By the way, I also set length: {maximum: 150}
.
Let's check the controller right away!
comments_controller.rb
class BookCommentsController < ApplicationController
before_action :authenticate_user! #Allow access only to logged-in users
def create
@book = Book.find(params[:book_id])
@comment = Comment.new(comment_params) #Validation check is applied at this timing!
@comment.book_id = @book.id
@comment.user_id = current_user.id
unless @comment.save #@If the comment could not be saved
render 'error' #comments/error.js.Calling erb(See below)
end
end
private
def comment_params
params.require(:comment).permit(:comment)
end
end
Using the value passed through the parameter, I am registering it in the database with the new method and save method. At that time, the foreign keys book_id and user_id cannot be obtained by parameters, so they are set at this timing. Well, is it all right so far?
The important thing here is that the validation check is done just before it is saved in the database, so the instance variable to get in the error statement is
@comment = Comment.new(comment_params)
Is that.
In other words, it checks whether the value itself that has passed the parameter contains an error.
Next, the comment section is on the details page of Book, so let's check the controller of Books!
books_controller.rb
class BookController < ApplicationController
before_action :authenticate_user! #Allow access only to logged-in users
def show
@book = Book.find(params[:id])
@comment = Comment.new #Comments new method for passing values to the controller
end
end
There will be no problem here either! You have reached the turning point. Let's check the details page of Book at this pace!
ruby:app/views/books/show.html.erb
<div id="comments_error">
<%= render 'layouts/errors', model: @comment %>
</div>
<%= form_with model:[@book,@comment] do |f| %>
<%= f.text_area :comment %>
<%= f.submit 'Send'%>
<% end %>
In the part of comments_error
in the first half, _errors.html.erb
of layouts is called by render.
Also, I have provided a comment input field using form_with
, but I want to set asynchronous communication, so I will not describe local: true
.
In the case of form_with
, asynchronous communication is used by default, so there is no need to write remote: true
.
ruby:app/views/layouts/_errors.html.erb
<% if model.errors.any? %>
<div id="error_explanation">
<h3>
<%= pluralize(obj.errors.count, "error") %> prohibited this model from being saved:
</h3>
<ul>
<% model.errors.full_messages.each do |message| %>
<li><%= message %></li>
<% end %>
</ul>
</div>
<% end %>
Do you remember calling with render from the Show page of Books?
The error message is finally here.
errors.full_messages
gets all the error messages as an array.
Since it is assumed that you are caught in multiple errors, we are looping with each method.
Since this error statement is included by default, it can be used in various situations other than the comment section.
ruby:app/views/comments/error.js.erb
$("#comments_error").html("<%= j(render 'layouts/errors', model: @comment) %>");
In books / show.html.erb
, it is the process to rewrite the part ofid = "comments_error"
.
Targeting id =" comments_error "
with $ ("# comments_error ")
, it is rewritten with the contents of layouts / _errors.html.erb
specified in render'layouts / errors'
.
In addition, it is @ comment
passed to model here, but do you know where this is defined?
You used to call the js file with the Comments controller.
That is, defined in the create action in the Comments controller,
@comment = Comment.new(comment_params)
Is passed to layouts / _errors.html.erb
.
Hmmm, it's complicated! !!
ruby:app/views/comments/create.js.erb
$(".comments").html("<%= j(render 'comments/index', book: @book) %>");
$("#comment_comment").val("");
By implementing asynchronous communication
If you try to send a comment with blank
as it is,
I was angry as I expected.
This completes the error message using asynchronous communication!
It is possible to convert the message text into Japanese, but due to space limitations, this is another opportunity.
Asynchronous communication has a lot of controllers and page-to-page transitions, so it's very confusing. If you read it a few times, I think it will bring you closer to understanding. If you get angry about the process flow, you may be able to shorten the implementation time. Excuse me. Let's study hard together!
Recommended Posts