目標
RailsでAjaxを用いてバリデーションに引っかかった時、エラーメッセージを出す。
Ajaxの例としてコメント機能で書きます。
環境
環境 rails 6.0.2.2
簡略的に、Userモデル(nameカラムとemailカラム)、Topicモデル(contentカラムとuser_idカラム)、Commentモデル(reviewカラムとuser_idとtopic_id)とします
Twitterに例えると、Userがツイート(Topic)し、そこにUserがCommentするといった感じです!
以下モデルの関連付けとバリエーション(Commentのみ)です。
has_many :topics, dependent: :destroy
has_many :comments, dependent: :destroy
belongs_to :user
has_many :comments, dependent: :destroy
belongs_to :user
belongs_to :topic
validates :review, presence: true, length: { maximum: 150 }
Commentモデルはreviewカラムが「からの時」と「151文字以上」の時にエラーになります。
コントローラー
まずコントローラーを書いていきます。
def show
@topic = Topic.find[:params]
@comment = Comment.new
@comments = @topic.comments
end
def create
@topic = Topic.find(params[:topic_id])
@comment = @topic.comments.build(comment_params)
@comment.user_id = current_user.id
@comment.save
end
def destroy
@comment = Comment.find(params[:id])
@comment.destroy
end
コメントはtopics/show.html.erbでできるようにしています。
ビュー
次はビューです
<div id="comments_area">
<%= render partial: 'comments/index', locals: { comments: @comments } %>
</div>
<div id="error_explanation">
</div>
<div id="form_area">
<% if current_user %>
<%= render partial: 'comments/form', locals: { comment: @comment, topic: @topic } %>
<% end %>
</div>
$("#comments_area").html("<%= j(render 'index', { comments: @comment.topic.comments }) %>")
$("#error_explanation").html("<%= j(render 'error', { comment: @comment }) %>")
$("textarea").val("")
<%= form_with(model: [topic, comment]) do |f| %>
<% if comment.errors.any? %>
<div id="error_explanation">
<ul>
<% comment.errors.full_messages.each do |message| %>
<li><%= message %></li>
<% end %>
</ul>
</div>
<% end %>
<div>
<%= f.label :review %><span style="color: #f00;">(150文字以内)</span><br>
<%= f.text_area :review,placeholder: "コメントはこちら" %>
</div>
<div class="actions mb-3">
<%= f.submit "コメントをする", class: "btn btn-x btn-outline-secondary" %>
</div>
<% end %>
<% comments.each do |comment| %>
<% unless comment.id.nil? %>
<p><%= link_to "#{comment.user.name}さん",user_path(comment.user.id) %></p>
<p><%= comment.review %></p>
<% if comment.user == current_user %>
<p><%= link_to "コメントを削除する",topic_comment_path(comment.topic_id,comment.id),method: :delete, remote: true %></p><hr size="5px" color="black">
<% end %>
<% end %>
<% end %>
<% if comment.errors.any? %>
<div class="error_message alert alert-warning">
<ul>
<% comment.errors.full_messages.each do |message| %>
<li><%= message %></li>
<% end %>
</ul>
</div>
<% end %>
流れとして
①topics/show.html.erbにアクセスすると、_form.html.erbと_index.html.erbが表示されている。indexはすでに保存されているコメントを表示しているhtml。
②formで「コメントをする」ボタンを押すと、commentコントローラーのcreateメソッドが実行される。この時、form_withはデフォルトでremote: trueになっているので、ビューは「create.js.erb」を探す。
(ちなみにlocal: trueとすればcreate.html.erbを探す)
③create.js.erbの1行目では、idがcomments_areaのところに_index.html.erbを表示させている。
2行目では同じように_error.html.erbを表示させている。ただエラーの方を見ると、if構文になっていて、エラーがなければ表示されない。3行目はformのtextareaを空にするコード。
これでエラーが表示されるようになります。
その他
一応ルートとコメント削除のビューも書いておきます。
resources :topics do
resources :comments, only: [:create, :destroy]
end
$("#comments_area").html("<%= j(render 'index', { comments: @comment.topic.comments }) %>")
$("textarea").val("")
以上です!何かおかしいところなどありましたら、ご指摘下さい!