コメント(編集)のAjax化について

どうもどハマりエンジニアです。

 

今回はコメント(編集)のAjax化について行っていきます。

 

流れとして下記の4つを実装していきます。

機能を四つに分けて実装していきます。
1. 編集ボタンクリックでラベル非表示、コメントエリアとボタン表示
2. キャンセルボタンクリックでラベル表示、コメントエリアとボタン非表示
3. 更新ボタンクリッ成功でDB更新、ラベル表示、コメントエリアとボタン非表示
4. 更新ボタンクリック失敗でエラーメッセージ表示

 

そもそもrailsに渡していた投稿内容の情報をjavascriptにどうやって渡すのか?

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) {
  $.ajax({
  type: 'PATCH',
  url: '/comments/' + commentId,
  data: {
  comment: {
  body: body
   }  
   }
   }).done(function (result) {
  resolve(result)
   }).fail(function (result) {
  reject(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