Stimulusの習作: ドラッグ・アンド・ドロップ(サーバ側)

外観

前回の続きです。今回はバックエンドのRailsと通信します。

画像1

仕様

ドラッグ終了イベントで、リスト順に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 %>

以上です。




この記事が気に入ったらサポートをしてみませんか?