Stimulusの習作: ドラッグ・アンド・ドロップ(サーバ側)
外観
前回の続きです。今回はバックエンドのRailsと通信します。
仕様
ドラッグ終了イベントで、リスト順にBook IDを集める。例:"2,3,1,4,5"
上記のBook ID群をクライアントからサーバへ送る。
エンドポイント
Bookのrow_order(行の表示順)を更新するインタフェースを追加します。
config/routes.rb
Rails.application.routes.draw do
resources :books do
patch 'row_order', on: :collection
end
end
Prefixなどを確認しておきます。(必要ならば)
bin/rails routes -c book
Prefix Verb URI Pattern Controller#Action
row_order_books PATCH /books/row_order(.:format) books#row_order
...
表示順の更新
サーバ側のコントローラのアクションを追加します。
引数で渡されたBook IDの順番で表示順を更新する。
ひとつでも失敗したらロールバックしたいのでトランザクションで囲む。
検証せずに保存する。# book.save!は意図して失敗させる時に使う予定。
今回は検証の有無を対称的に記述したかったので更新メソッドは使用せず。
app/controllers/books_controller.rb
# PATCH /books/row_order(.:format)
def row_order
ActiveRecord::Base.transaction do
params[:row_order].split(',').each_with_index do |id, i|
book = Book.find(id)
book.row_order = i + 1
book.save!(validate: false)
# book.save!
end
end
end
Book IDの送信
クライアント側のコントローラのメソッドを追加します。
ドラッグ終了イベントで、リスト順にBook IDを集める。
上記のBook ID群をクライアントからサーバへ送る。
app/javascript/controllers/drag_item_controller.js
dragend(event) {
const nodes = this.element.querySelectorAll('[data-book-id]')
const ids = Array.from(nodes).map(el => el.getAttribute('data-book-id'))
const form = document.getElementById('row-order-form')
document.getElementById('row_order').value = ids
Rails.fire(form, 'submit')
}
今回は隠しフォームを使いました。
app/views/books/index.html.erb
<div class="hidden">
<%= form_with id: 'row-order-form', method: :patch, url: row_order_books_path do |form| %>
<%= form.text_field :row_order %>
<% end %>
</div>
app/assets/stylesheets/books.scss
.hidden {
display: none;
}
エラー処理
更新に失敗したらページを読み直す。
app/javascript/controllers/drag_item_controller.js
onPostError(event) {
Turbolinks.visit('/books')
}
ajax:errorはグローバルイベントなので@documentを付ける。
app/views/books/index.html.erb
<% action = propagate_events(controller, actions) %>
<% action << ' ajax:error@document->drag-item#onPostError' %>
<%= tag.ul data: { controller: controller, action: action } do %>
以上です。
この記事が気に入ったらサポートをしてみませんか?