どうもどハマりエンジニアです。
今回はコメント(編集)のAjax化について行っていきます。
流れとして下記の4つを実装していきます。
機能を四つに分けて実装していきます。
1. 編集ボタンクリックでラベル非表示、コメントエリアとボタン表示
2. キャンセルボタンクリックでラベル表示、コメントエリアとボタン非表示
3. 更新ボタンクリック成功でDB更新、ラベル表示、コメントエリアとボタン非表示
4. 更新ボタンクリック失敗でエラーメッセージ表示
railsで定義した変数をJavaScriptに渡すためにはjson形式に変換して渡す必要があります。
まずは、編集ボタンクリックでラベル非表示、コメントエリアとボタンの表示とその逆の実装をしていく。
(comments/_comment.html.erb)
<td>
<h3 class="small"><%= comment.user.decorate.full_name %></h3>
<div id="js-comment-<%= comment.id %>">
<p><%= simple_format(comment.body) %></p>
</div>
<div id="js-textarea-comment-box-<%= comment.id %>"
style="display: none;">
<textarea id="js-textarea-comment-<%= comment.id %>"
class="form-control mb-1"><%= comment.body %></textarea>
<button class="btn btn-light js-button-edit-comment-cancel"
data-comment-id="<%= comment.id %>">キャンセル</button>
<button class="btn btn-success js-button-comment-update"
data-comment-id="<%= comment.id %>">更新</button>
</div>
</td>
class="js-button-comment-update" → jsに使う
data-comment-id → dataは属性として使う。また一つ一つにコメントのID必要が あるためIDをひっぱてくる
(comments_controller)
def update
@comment = current_user.comments.find(params[:id])
if @comment.update(comment_update_params)
render json: { comment: @comment }, status: :ok
else
render json: { comment: @comment, errors:
{ messages: @comment.errors.full_messages } },
status: :bad_request
end
end
private
def comment_update_params
params.require(:comment).permit(:body)
end
(assets/javascripts/edti_comments.js)
$(function() {
$(document).on("click", '.js-edit-comment-button', function(e) {
e.preventDefault();
const commentId = $(this).data("comment-id")
switchToEdit(commentId)
})
$(document).on("click", '.js-button-edit-comment-cancel', function() {
clearErrorMessages()
const commentId = $(this).data("comment-id")
switchToLabel(commentId)
})
$(document).on("click", '.js-button-comment-update', function() {
clearErrorMessages()
const commentId = $(this).data("comment-id")
submitComment($("#js-textarea-comment-" + commentId).val(), commentId)
.then(result => {
$("#js-comment-" + result.comment.id).html(result.comment.body.replace(/\r?\n/g, '<br>'))
switchToLabel(result.comment.id)
})
.catch(result => {
const commentId = result.responseJSON.comment.id
const messages = result.responseJSON.errors.messages
showErrorMessages(commentId, messages)
})
})
function switchToLabel(commentId) {
$("#js-textarea-comment-box-" + commentId).hide()
$("#js-comment-" + commentId).show()
}
function switchToEdit(commentId) {
$("#js-comment-" + commentId).hide()
$("#js-textarea-comment-box-" + commentId).show()
}
function showErrorMessages(commentId, messages) {
$('<p class="error_messages text-danger">' + messages.join('<br>') + '</p>').insertBefore($("#js-textarea-comment-" + commentId))
}
function submitComment(body, commentId) {
return new Promise(function(resolve, reject) {
type: 'PATCH',
url: '/comments/' + commentId,
data: {
comment: {
body: body
}
}
}).done(function (result) {
resolve(result)
}).fail(function (result) {
});
})
}
function clearErrorMessages() {
$("p.error_messages").remove()
}
});
function()関数の中で、Promiseオブジェクトを生成しています。Promiseの引数にresolveとrejectがありますが、これが非同期処理が実行された場合のコールバック関数となります。
resolveは処理が正常に終了した時に呼ばれるコールバック関数です。また上記では記述されていませんが、rejectは処理が失敗した時に呼ばれるコールバック関数となります。resolveに引数がありますが、このように任意のオブジェクトを渡すことができます。
then関数とcatch関数が記述されています。thenは、処理が正常に実行された時、すわなちresolveが実行された時に呼ばれ、resolveの引数が渡されます。catch関数は上記では記述されていませんが、rejectが実行された時に呼ばれます。
replace(/\r?\n/g, '');
これは改行コード(\r\nまたは\n)をすべて削除するには次のように記述します。
<参考資料>
http://js.studio-kingdom.com/javascript/function/
https://qiita.com/hiro266/items/160a4f9290ecd9f27d15
https://pikawaka.com/rails/json
https://noumenon-th.net/programming/2018/12/14/promise1/
https://qiita.com/aiandrox/items/f612b9b8503785ed18bf