はじめに
ブックマーク処理に続いて、コメント投稿、削除機能もAjax化するやり方を見ていきましょう。
コメント投稿機能は実装済みとします。
作業の流れ
- コメント作成、削除処理のAjax化
- コメント削除のルーティングの追加
- commentsコントローラを修正
- コメント作成、削除時のレンダリング処理を行うjsファイルを用意する
コメント作成、削除処理のAjax化
コメント投稿フォームの処理をAjax化する処理を追加
<%= form_with model: comment, url: [board, comment], id: 'new_comment' do |f| %>
<%= f.label :body %>
<%= f.text_area :body, class: 'form-control mb-3', rows: 4, placeholder: Comment.human_attribute_name(:body), id: 'js-new-comment-body' %>
<%= f.submit class: 'btn btn-primary' %>
<% end %>
コメント投稿フォームの処理をAjax化するときは、form_withにremote: true
オプションにします。なお、form_with
は元々デフォルトでremote: true
が機能しているので、わざわざ書かなくてもAjax処理が選択されます。同期通信をしたい場合はlocal :true
を入れましょう。
また、コメント作成時のエラーメッセージの出し分けはAjax処理で行うので、 <%= render 'shared/error_messages', object: f.object %>
の一文は削除します。create処理時にコメント投稿時のエラーメッセージを埋め込む位置を特定するため、form_with
にはid:new_comment
を追加しておきましょう。
投稿処理後に入力フォームに記載したコメントの内容が消される処理をするため、textareaフォームにはid: js-new-comment-body
を追加しましょう。
コメント削除するリンクにAjaxでサーバにリクエストを送信する処理を追加
<li class="list-inline-item">
<%= link_to comment_path(comment), class: 'js-delete-comment-button', method: :delete, data: { confirm: t('defaults.message.delete_confirm') }, remote: true do %>
<%= icon 'fa', 'trash' %>
<% end %>
</li>
リンク先をcomment_path(comment)
とし、remote :true
を追加します。
コメント削除のルーティングを追加
コメント削除のルーティングを追記しましょう
resources :boards , shallow: true do
resources :comments, only: %i[create destroy]
collection do
get 'bookmarks'
end
end
resources :bookmarks, only: %i[create destroy]
commentコントローラを修正
class CommentsController < ApplicationController
def create
@comment = current_user.comments.build(comment_params)
@comment.save
end
def destroy
@comment = current_user.comments.find(params[:id])
@comment.destroy!
end
リダイレクト処理の部分をまるごと削除し、ビューへ変数の値を渡すためにインスタンス変数にしています。
コメント削除処理は失敗を想定した設計となっていないので destroy!
と書いてもよいが、save!
としてはいけません。コメント保存失敗時に例外が起きてしまい、処理がcreate.js.erb
にいかなくなります。
コメント作成、削除時のレンダリング処理を行うjsファイルを用意する
エラーメッセージテンプレートにIDを追加
<% if object.errors.any? %>
<div class="alert alert-danger", id="error_messages">
<ul class="mb-0">
<% object.errors.full_messages.each do |msg| %>
<li><%= msg %></li>
<% end %>
</ul>
</div>
<% end %>
空欄で投稿して処理が失敗した場合に「コメントを入力してください」というメッセージが投稿フォーム上部に表示されるようにしたので、jsファイルで判定処理を書くための id="error_messages"
を追加します。
Ajaxによるコメント作成
# 既に表示されているエラーメッセージがあった場合は削除する
$("#error_messages").remove()
# コメント作成処理の結果によって処理を分岐させる
<% if @comment.errors.present? %>
# エラーがある、処理失敗時にはエラーメッセージのパーシャルを表示させる
$("#new_comment").prepend("<%= j(render('shared/error_messages', object: @comment)) %>")
<% else %>
# エラーがない、処理成功時には作成されたコメント内容をHTML要素として追加する
$("#js-table-comment").prepend("<%= j(render('comments/comment', comment: @comment)) %>")
# コメント入力フォームのテキストは表示する必要がないので、空文字に置き換えて内容をクリアする
$("#js-new-comment-body").val('')
<% end %>
コードを紐解く
prependメソッドの挙動
prependメソッドは、指定の要素の子要素の先頭に追加します。
$(追加先).prepend(追加する要素)
つまり追加先の要素の中に入るということです。
valメソッドの挙動
投稿成功時に最後にコメント入力フォームのvalue属性を空文字を指定して返しています。
$("#js-new-comment-body").val('')
を記載しないと、入力したコメントが、投稿後も残り続けてしまいます。この記述のおかげで、投稿処理後、入力フォームに記載したコメントの内容が消されることになります。
Ajaxによるコメント削除
どちらの書き方でも良いです。
ES6(JavaScript2015)からはd末尾に;
を付けない記法もサポートされているみたいです。
$('#<%= "comment-#{@comment.id}"%>').remove();
$("tr#comment-<%= @comment.id %>").remove()
/views/comments/_comment.html.erb
の先頭の、
<tr id="comment-<%= comment.id %>">
の部分を取ってきています。
終わりに
jsファイルのjQueryの処理は難しかったと思いますが、しっかり流れを掴んで理解しましょう。
コメント