A stimulus sortable controller

The goal: make a list of tasks sortable. @shopify/draggable takes the client part, turbostream re-renders the list after the update. The positioning in active record uses acts_as_list.

Models

class List < ApplicationRecord
  has_many :tasks, -> { order(:position) }, dependent: :restrict_with_error

  validates_presence_of :name

  def to_s
    "#{name}"
  end
end
class Task < ApplicationRecord
  default_scope { order(:position) }
  belongs_to :list, optional: false

  acts_as_list scope: :list
  validates_presence_of :subject
end

Views

The following snippets shows only the relevant parts. For the full views please have a look as rails-playground/app/views/lists and rails-playground/app/views/tasks.

lists/show.html.erb

<div id="list_tasks"> (1)
  <%= render partial: 'lists/show', locals: { list: @list }%>
</div>
1 a unique id use with turbo_stream.update in app/views/tasks/update.turbo_stream.erb

lists/_show.html.erb

<div data-controller="sortable"  (1)
     data-sortable-handle-value=".handle" (2)
     class="list-group mb-3">
  <%= render partial: 'tasks/task', collection: list.tasks, as: :task %>
</div>
1 initialize the sortable controller
2 optional: set handle to restrict draggable to some parts of your item.

tasks/_task.html.erb

<div id="<%= dom_id task %>"  (1)
     class="list-group-item"
     data-sortable-target="item" (2)
     data-url="<%= polymorphic_path([task.list, task])%>" (3)
>
...
1 id must be dom_id(task)
2 stimulus target is item.
3 path for update via PUT