【Rails】コメント編集機能(非同期通信化)
コメント機能にAjaxで編集機能をもたせます!どなたかの参考になりますように。
イメージ
ER図
こんな感じにしています。
前提
- コメントの投稿機能ができている。(この記事ではCommentモデル)
流れ
- ルーティング作成
- コントローラ作成
- ビュー作成
- JavaScript作成
ルーティングの追加
updateアクションをresources :comments
内に追加し、コメント更新ができるようにします。
:
scope module: :public do
resources :comments, only: [:create, :destroy, :update]
end
:
コントローラの追加
コントローラにもupdateを追記してあげます。
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
解説:
-
@comment.reload
reload
メソッドを使うことで、更新に失敗した場合に元に戻すようにする。 -
unless @comment.update(comment_update_params)
comment_update_params
でどの情報を更新するかを指定する。更新が成功すると、update
メソッドはtrue
を返し、unless
を使うことで、条件がfalse
の場合にreloadするようにする。
ビュー
編集ボタンを押すと、編集フォームが開くようにJavaScriptのコードを作ります。そして非同期通信化するため、コメント編集フォームは部分テンプレートにしてあげます。
編集ボタンとコメント編集フォームの部分テンプレートの呼び出し
ビュー内で、編集ボタンとコメント編集フォームを部分テンプレートとして呼び出すコードを示しています。
:
<% 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で要素を特定できるようにします。
コメント編集フォーム
編集フォーム部分を部分テンプレートで作成します。
<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 %>
解説:
-
<p id="js-comment-label-<%= comment.id %>">...</p>
コメントのテキストを表示するための<p>
要素。id
属性にはjs-comment-label-<%= comment.id %>
を設定して、JavaScriptで特定のコメントを識別できるようにする。コメントの実際のテキストは<%= comment.comment %>
で挿入する。 -
<p id="js-comment-post-error-<%= comment.id %>" class="text-danger"></p>
コメントの更新時にエラーメッセージを表示するための要素。 -
<%= form_with model: comment, local: false do |f| %> ... <% end %>
form_with
メソッドを使用して、comment
モデルに関連付けられたフォームを作成。local: false
で非同期で送信できるようにする。 -
<%= f.text_area ... %>
フォーム内のテキストエリアを表示。このエリアでコメントの編集ができる。display: none
することにより、初めは非表示状態に設定。
非同期化
// コメントの更新が成功した場合
$('#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 %>
解説:
コメントの非同期更新時に以下のことを行うようにします。
- コメントの内容を更新
- 編集キャンセルボタンがクリックされた場合に、表示を元に戻す
- コメントの更新時にエラーがある場合、エラーメッセージを表示して2秒後にフェードアウトさせる
JavaScript
JavaScriptでコメント編集部分の発火と解除ができるようにします。
// コメント編集発火 //
$(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();
});
})
解説:
-
編集ボタンのクリック処理:
編集ボタンがクリックされたとき、クリックされたボタンの親要素からコメントIDを取得し、どのコメントを編集するかを特定してあげます。
対応するコメントの編集用テキストエリア、更新ボタンを表示/非表示を切り替えます。 -
"キャンセルボタン" のクリック処理:
キャンセルボタンがクリックされたとき、クリックされたボタンのデータ属性からコメントIDを取得し、編集フォーム要素を非表示にします。
今日がスクールのオンライン教室の最後の使用日でした。
最後に同期生と話せてよかった!
同期生が皆無事に今後の進路が決まりますように!
Discussion