作成・削除のAjax化、エラーの処理の分岐
課題メモ
↑で学んだことをかなり活用してみたが、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
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アクションから消したエラーの分岐をこちらに記載
破壊的メソッド!
失敗時の処理を指定しない場合はメソッドに!マークをつけて例外を発生させるようにするべき。
!マークをつけない場合、例外は発生せずに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()