Rails Diary

プログラミングの学習記録です。

作成・削除のAjax化、エラーの処理の分岐

課題メモ

okmt-aya-26.hatenablog.com

  • ↑で学んだことをかなり活用してみたが、appendメソッドを使うと、非同期通信にならずにprependメソッドに変更した。こちらだと上手く動作したが理由が分からず

  • フォームの中身を消す処理でresetメソッドが上手く動かなかったので、val("")での置き換えをした

  • js処理のセレクタチョイスが難しい。これかな?と思うセレクタを選んで指定するも動かなかったので、探すのに時間がかかった。検証ツールを使ってHTMLにコンパイルされたコードからidを探してきた。必要に応じて適切なidを記載する。

  • 解答のセレクタとほぼ違っていたが、動作的には問題なかった。なんで?

自分が行った実装

  • form_withやlink_toのAjax化済み(local: true削除やremote: true追加)
  • destroyアクションへのルーティング追加
① commentsコントローラにcreataアクション追加
def create
    @comment = current_user.comments.build(comment_params)
    if @comment.save
      render :create
    else
      render :errors
    end
  end

↑自分はここでレンダリングするファイルの分岐を入れ、エラーを入力フォーム上部に挿入する処理ファイルを作っていたが、create.js.erbにエラーがあった時なかった時の分岐を入れれば、わざわざerrors.js.erbファイルを作らなくて済む。

② createアクションで呼び出される処理を追加

_comments.html.erb

<div class="row">
  <div class="col-lg-8 offset-lg-2">
    <table id="js-table-comment" class="table"> <!--テーブルのID-->
      <%= render comments %>
    </table>

↓テーブルのIDで記載されているjs-table-commentセレクタに使用
create.js.erb

$('#js-table-comment').prepend("<%= j(render 'comment', comment: @comment) %>");
$('#comment_body').val("")
  • idがjs-table-commentの親要素(comments)にprependメソッドでcommentパーシャルを追加する処理
  • コメントフォームの中身を空にする処理
comments/_form.html.erb

↓コメントフォームにエラーの挿入箇所記載

<div class="js-comment-errors"></div> <!--追加-->
<div class="row mb-3">
    <div class="col-lg-8 offset-lg-2">
      <%= form_with model: comment, url: [board, comment] do |f| %>
        <%= render 'shared/error_messages', object: f.object %>
        <%= f.label :body %>
(中略)
errors.js.erb
$('.js-comment-errors').replaceWith("<%= j(render 'shared/error_messages', object: @comment )%>");

エラー時には以前作ったエラーメッセージのパーシャルにセレクタで指定した箇所を置き換える処理を追加。

③ destroyアクション追加
def destroy
    @comment = current_user.comments.find_by(user_id: current_user, 
                                         board_id: params[:board_id])
    @comment&.destroy
end
  • メソッドのレシーバーがnilだとエラーが出てしまったのでぼっち演算子使用した

destroy.js.erb

$("#js-comment-25").remove();

これで要件通り機能するようにはなったけど・・・

エラー処理の無駄を省く

_form.html.erb

<div class="js-comment-errors"></div> <!--削除-->
<div class="row mb-3">
    <div class="col-lg-8 offset-lg-2">
      <!--form_with内にjavascriptで操作する用のidを追加-->
      <%= form_with model: comment, url: [board, comment], id: new_comment do |f| %>  
        <%= render 'shared/error_messages', object: f.object %>  <!--削除-->
        <%= f.label :body %>
(中略)

前に作った_error_messages.html.erbを利用

<% if object.errors.any? %>
    <div id="error_messages" class="alert alert-danger">
      <ul class="mb-0">
        <% object.errors.full_messages.each do |msg| %>
          <li><%= msg %></li>
        <% end %>
      </ul>
    </div>
<% end %>

↑のid: error_messagesを使って、error_messageが出ていた場合removeする処理をまず記載

create.js.erb

$("#error_messages").remove()
<% if @comment.errors.present? %>
  $("#new_comment").prepend("<%= j(render('shared/error_messages', object: @comment))%>")
<% else %>
  $('#js-table-comment').prepend("<%= j(render 'comment', comment: @comment) %>");
  $('#comment_body').val("")
<% end %>

↑commentのcreateアクションから消したエラーの分岐をこちらに記載

破壊的メソッド!

okmt-aya-26.hatenablog.com

  • 失敗時の処理を指定しない場合はメソッドに!マークをつけて例外を発生させるようにするべき。

  • !マークをつけない場合、例外は発生せずにfalseを返すだけなので、後続の処理が実行され意図しない挙動をすることになる。

.valid?メソッドを実行した時同様に、対象オブジェクトのerrorsメソッドにバリデーションエラーの内容を登録する。

destroyメソッド

★destroyメソッドにはエラーハンドリングを施していなかったので、破壊的メソッドにするべきだった。

ぼっち演算子を使い、js.erbファイルのセレクタにs-comment-25を記載しても削除できてしまった。 どのコメントも区別がなくなってしまうのでコメントパーシャル上部のidをコメントのIDごとに変更する<tr id="js-comment-<%= comment.id %>">

destroy.js.erb

$("tr#comment-<%= @comment.id %>").removea()