🍚

【Rails】コメント編集機能(非同期通信化)

2023/09/25に公開

コメント機能にAjaxで編集機能をもたせます!どなたかの参考になりますように。

イメージ

ER図

こんな感じにしています。

前提

  • コメントの投稿機能ができている。(この記事ではCommentモデル)

流れ

  1. ルーティング作成
  2. コントローラ作成
  3. ビュー作成
  4. JavaScript作成

ルーティングの追加

updateアクションをresources :comments内に追加し、コメント更新ができるようにします。

routes.rb
:
scope module: :public do
 resources :comments, only: [:create, :destroy, :update]
end
:

コントローラの追加

コントローラにもupdateを追記してあげます。

comments_controller.rb
class Public::CommentsController < ApplicationController
  before_action :set_comment, only: [:update, :destroy]
  before_action :authenticate_user!

 :

  def update
    @comment.reload unless @comment.update(comment_update_params)
  end

  private

  def set_comment
    @comment = current_user.comments.find(params[:id])
  end

  def comment_update_params
    params.require(:comment).permit(:comment)
  end
 :
end

解説:

  1. @comment.reload
    reloadメソッドを使うことで、更新に失敗した場合に元に戻すようにする。

  2. unless @comment.update(comment_update_params)
    comment_update_params でどの情報を更新するかを指定する。更新が成功すると、update メソッドは true を返し、unlessを使うことで、条件が false の場合にreloadするようにする。

ビュー

編集ボタンを押すと、編集フォームが開くようにJavaScriptのコードを作ります。そして非同期通信化するため、コメント編集フォームは部分テンプレートにしてあげます。

編集ボタンとコメント編集フォームの部分テンプレートの呼び出し

ビュー内で、編集ボタンとコメント編集フォームを部分テンプレートとして呼び出すコードを示しています。

xxxx.html.erb
:
<% if user_signed_in? && (current_user == comment.user) %>
 <span data-comment-id=<%= comment.id %> class="js-edit-comment-button">
  <i class="fas fa-edit"></i>
 </span>
 
<div id="js-comment-<%= comment.id %>">
 <%= render "public/comments/js_comment", comment: comment %>
</div>
:

解説:
data-comment-idは属性名で、その値として<%= comment.id %>を設定。これによりコメントに識別子(ID)をつけてあげ、JavaScriptで要素を特定できるようにします。

コメント編集フォーム

編集フォーム部分を部分テンプレートで作成します。

_js_comment.html.erb
<p id="js-comment-label-<%= comment.id %>"><%= comment.comment %></p>
<p id="js-comment-post-error-<%= comment.id %>" class="text-danger"></p>

<%= form_with model: comment, local: false do |f| %>
  <%= f.text_area :comment, id: "js-textarea-comment-#{comment.id}",
                            class: "form-control comment-post-error",
                            style: "display:none;" %>

  <div id="js-comment-button-<%= comment.id %>" style="display: none;">
    <button data-cancel-id=<%= comment.id %> type="button" class="comment-button comment-cancel-button">キャンセル</button>
    <%= f.submit "更新", class: "comment-button comment-update-button" %>
  </div>
<% end %>

解説:

  1. <p id="js-comment-label-<%= comment.id %>">...</p>
    コメントのテキストを表示するための <p> 要素。id 属性には js-comment-label-<%= comment.id %> を設定して、JavaScriptで特定のコメントを識別できるようにする。コメントの実際のテキストは <%= comment.comment %> で挿入する。

  2. <p id="js-comment-post-error-<%= comment.id %>" class="text-danger"></p>
    コメントの更新時にエラーメッセージを表示するための要素。

  3. <%= form_with model: comment, local: false do |f| %> ... <% end %>
    form_with メソッドを使用して、comment モデルに関連付けられたフォームを作成。local: false で非同期で送信できるようにする。

  4. <%= f.text_area ... %>
    フォーム内のテキストエリアを表示。このエリアでコメントの編集ができる。display: noneすることにより、初めは非表示状態に設定。

非同期化

update.js.erb
// コメントの更新が成功した場合
$('#js-comment-<%= @comment.id %>').html('<%=j render "public/comments/js_comment", comment: @comment %>');

// 編集キャンセルボタンがクリックされた時の処理
$($('#js-comment-<%= @comment.id %>').find('.comment-cancel-button')[0]).on('click', (e) => {
    // コメントのラベルを再表示
    $('#js-comment-label-<%= @comment.id %>').show();
    // コメントの編集用テキストエリアを非表示
    $('#js-textarea-comment-<%= @comment.id %>').hide();
    // コメントの更新ボタンを非表示
    $('#js-comment-button-<%= @comment.id %>').hide();
    // エラーメッセージを非表示
    $('#js-comment-post-error-<%= @comment.id %>').hide();
})

// コメントの更新時にエラーがある場合
<% if @comment.errors.any? %>
  // エラーメッセージ要素を取得
  const commentError = $('#js-comment-post-error-<%= @comment.id %>');
  // エラーメッセージを設定
  commentError.text('<%= @comment.errors.full_messages.first %>');
  // エラーメッセージを2秒でフェードアウト
  commentError.fadeOut(2000);
<% end %>

解説:
コメントの非同期更新時に以下のことを行うようにします。

  1. コメントの内容を更新
  2. 編集キャンセルボタンがクリックされた場合に、表示を元に戻す
  3. コメントの更新時にエラーがある場合、エラーメッセージを表示して2秒後にフェードアウトさせる

JavaScript

JavaScriptでコメント編集部分の発火と解除ができるようにします。

xxxx.js
// コメント編集発火 //
$(document).on("turbolinks:load", () => {
  // 編集ボタンがクリックされた時の処理
  $("body").on("click", ".js-edit-comment-button",(e) => {
    // クリックされた編集ボタンの親要素からコメントIDを取得
    const commentId = $(e.target).parent().data('commentId');                   
    // 対応するコメントのラベル要素を取得
    const commentLabelArea = $('#js-comment-label-' + commentId);  
    // 対応するコメントの編集用テキストエリアを取得
    const commentTextArea = $('#js-textarea-comment-' + commentId); 
    // 対応するコメントの更新ボタンを取得
    const commentButton = $('#js-comment-button-' + commentId);   
    
    // コメントのラベルを非表示にする
    commentLabelArea.hide();
    // コメントの編集用テキストエリアを表示する
    commentTextArea.show(); 
    // コメントの更新ボタンを表示する
    commentButton.show(); 
  });

  // キャンセルボタンがクリックされた時の処理
  $("body").on("click", ".comment-cancel-button", (e) => {
    // クリックされたキャンセルボタンのデータ属性からコメントIDを取得
    const commentId = $(e.target).data('cancel-id');
    // 対応するコメントのラベル要素を取得
    const commentLabelArea = $('#js-comment-label-' + commentId);
    // 対応するコメントの編集用テキストエリアを取得
    const commentTextArea = $('#js-textarea-comment-' + commentId);
    // 対応するコメントの更新ボタンを取得
    const commentButton = $('#js-comment-button-' + commentId);
    // 対応するコメントのエラーメッセージ要素を取得
    const commentError = $('#js-comment-post-error-' + commentId);

    // コメントのラベルを再表示する
    commentLabelArea.show();
    // コメントの編集用テキストエリアを非表示にする
    commentTextArea.hide();
    // コメントの更新ボタンを非表示にする
    commentButton.hide();
    // コメントのエラーメッセージを非表示にする
    commentError.hide();
  });
})

解説:

  1. 編集ボタンのクリック処理:
    編集ボタンがクリックされたとき、クリックされたボタンの親要素からコメントIDを取得し、どのコメントを編集するかを特定してあげます。
    対応するコメントの編集用テキストエリア、更新ボタンを表示/非表示を切り替えます。

  2. "キャンセルボタン" のクリック処理:
    キャンセルボタンがクリックされたとき、クリックされたボタンのデータ属性からコメントIDを取得し、編集フォーム要素を非表示にします。


今日がスクールのオンライン教室の最後の使用日でした。
最後に同期生と話せてよかった!
同期生が皆無事に今後の進路が決まりますように!

Discussion