[Rails] コメント作成、削除機能のAjax化

学習記録

はじめに

ブックマーク処理に続いて、コメント投稿、削除機能も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(追加する要素)

つまり追加先の要素の中に入るということです。

jQuery 要素を追加/子要素の先頭最後(append) | ITSakura

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の処理は難しかったと思いますが、しっかり流れを掴んで理解しましょう。

参考記事

jQuery 要素を追加/子要素の先頭最後(append) | ITSakura

http://semooh.jp/jquery/api/manipulation/prepend/content/

http://semooh.jp/jquery/api/attributes/val/val/

コメント

タイトルとURLをコピーしました