ブックマークボタンのajax化

ajaxとは

非同期通信と呼ばれる通信方法、通常、クライアントがリクエストを出したらサーバーからのレスポンスが返ってくるまで他の作業をすることができない同期通信と呼ばれる方法が用いられているが、クライアントからサーバーに対して、一部の情報だけを返すようにリクエストを送ることで、それ以外の部分は変わらないので他の作業ができる

ブックマークボタンをクリック時にajax通信を行うには

#_bookmark.html.erb
<%= link_to bookmarks_path(board_id: board.id), id: "js-bookmark-button-for-board-#{board.id}", class:"float-right", method: :post do %>
   <%= icon 'far', 'star' %>
<% end %>

#_unbookmark.html.erb
<%= link_to bookmark_path(current_user.bookmarks.find_by(board_id: board.id)), id: "js-bookmark-button-for-board-#{board.id}", class:"float-right", method: :delete do %>
   <%= icon 'fas', 'star' %>
<% end %>

と現在のブックマークボタンの状態でクリックすると

サーバーログ
Processing by BookmarksController#create as HTML

のように通常のブックマーク機能だとHTML形式でリクエストが送信される ここで、ブックマークのリンクヘルパーにremote: trueを指定することでJS形式でリクエストを送信できる

#_bookmark.html.erb
<%= link_to bookmarks_path(board_id: board.id),
             id: "js-bookmark-button-for-board-#{board.id}",
             class:"float-right",
             method: :post,
             remote: true do %>
   <%= icon 'far', 'star' %>
<% end %>

#_unbookmark.html.erb
<%= link_to bookmark_path(current_user.bookmarks.find_by(board_id: board.id)),
             id: "js-bookmark-button-for-board-#{board.id}",
             class:"float-right",
             method: :delete,
             remote: true do %>
   <%= icon 'fas', 'star' %>
<% end %>

これによりブックマークボタンをクリックすると

サーバーログ
Processing by BookmarksController#create as JS

となり、処理の流れは、bookmarks_controllerのcreate,destroyアクション の通過後はcreate.js.erb,destroy.js.erbファイルに向かうようになる

bookmarks_controller.rbの修正

修正前

#bookmarks_controller.rb
class BookmarksController < ApplicationController
  def create
    @board = Board.find(params[:board_id])
    current_user.bookmark(@board)
    redirect_back fallback_location: root_path, success: t('.success')
  end

  def destroy
    @board = current_user.bookmarks.find(params[:id]).board
    current_user.unbookmark(@board)
    redirect_back fallback_location: root_path, success: t('.success')
  end
end

修正後

#bookmarks_controller.rb
class BookmarksController < ApplicationController
  def create
    @board = Board.find(params[:board_id])
    current_user.bookmark(@board)
  end

  def destroy
    @board = current_user.bookmarks.find(params[:id]).board
    current_user.unbookmark(@board)
  end
end

とredirect_backの記述を削除

ブックマーク切り替えの処理のJSファイルを作成

HTML形式のリクエストで送信していた時はcreateアクションやdestroyアクションでビューの作成はしていないのでリダイレクト処理でどのページに遷移するか指定していたが、今はremote: trueでJS形式のリクエストで送信しているのでアクション通過後に部分的に更新させるためのJSファイルを作成する

#views/bookmarks/create.js.erb
$("#js-bookmark-button-for-board-<%= @board.id %>").replaceWith("<%= j(render('boards/unbookmark', board: @board)) %>");

#views/bookmarks/destroy.js.erb
$("#js-bookmark-button-for-board-<%= @board.id %>").replaceWith("<%= j(render('boards/bookmark', board: @board)) %>");

それぞれ、replaceWithでboards/unbookmark、boards/bookmarkをrenderして置き換えているが、javascripthは''や""があるとダメなのでエスケープする必要があるのでj(render~とexcape_javascript(renderのaliasを使っている、replaceWithと似たメソッドとしてhtmlメソッドがあるが、htmlにすると、指定している要素(今回はlink_toで生成されるaタグ)に設定したした値をセットする、今回それぞれ、指定している要素はbookamarkファイルとunbookmarkファイル内に含まれていて、それぞれ設定した値はbookmarkファイルとunbookmarkファイルをセットするので、link_toで生成されたaタグにネストされた形になってしまう。htmlは指定した要素に引数で渡した値をセットするのに、replaceWithは指定した要素に引数で渡した値を置き換えるといった違いがある

参考文献

Rails で JavaScript を使用する - Railsガイド

初心者目線でAjaxの説明 - Qiita

【Rails】ブックマーク機能のajax化 - Qiita

replaceWith(content) - jQuery 日本語リファレンス

escape_javascriptメソッドって何ぞや。 - Qiita