# Super Scaffolding with the `--sortable` option When issuing a `rails generate super_scaffold` command, you can pass the `--sortable` option like this: ``` # E.g. Pages belong to a Site and are sortable via drag-and-drop: rails generate super_scaffold Page Site,Team name:text_field path:text_area --sortable ``` The `--sortable` option: 1. Wraps the table's body in a `sortable` Stimulus controller, providing drag-and-drop re-ordering; 2. Adds a `reorder` action to your resource via `include SortableActions`, triggered automatically on re-order; 3. Adds a `sort_order` attribute to your model to store the ordering; 4. Adds a `default_scope` which orders by `sort_order` and auto increments `sort_order` on create via `include Sortable` on the model. ## Disabling Saving on Re-order By default, a call to save the new `sort_order` is triggered automatically on re-order. ### To disable auto-saving Add the `data-sortable-save-on-reorder-value="false"` param on the `sortable` root element: ```html
``` ### To manually fire the save action via a button Since the button won't be part of the `sortable` root element's descendants (all its direct descendants are sortable by default), you'll need to wrap both the `sortable` element and the save button in a new Stimulus controlled ancestor element. ```js /* sortable-wrapper_controller.js */ import { Controller } from "@hotwired/stimulus" export default class extends Controller { static targets = [ "sortable" ] saveSortOrder() { if (!this.hasSortableTarget) { return } this.sortableTarget.dispatchEvent(new CustomEvent("save-sort-order")) } } ``` On the button, add a `data-action` ```html ``` And on the `sortable` element, catch the `save-sort-order` event and define it as the `sortable` target for the `sortable-wrapper` controller: ```html ``` ## Events Under the hood, the `sortable` Stimulus controller uses the [dragula](https://github.com/bevacqua/dragula) library. All of the events that `dragula` defines are re-dispatched as native DOM events. The native DOM event name is prefixed with `sortable:` | dragula event name | DOM event name | |---------------------|----------------------| | drag | sortable:drag | | dragend | sortable:dragend | | drop | sortable:drop | | cancel | sortable:cancel | | remove | sortable:remove | | shadow | sortable:shadow | | over | sortable:over | | out | sortable:out | | cloned | sortable:cloned | The original event's listener arguments are passed to the native DOM event as a simple numbered Array under `event.detail.args`. See [dragula's list of events](https://github.com/bevacqua/dragula#drakeon-events) for the listener arguments. ### Example: Asking for Confirmation on the `drop` Event Let's say we'd like to ask the user to confirm before saving the new sort order: > Are you sure you want to place DROPPED ITEM before SIBLING ITEM? ```js /* confirm-reorder_controller.js */ import { Controller } from "@hotwired/stimulus" export default class extends Controller { static targets = [ "sortable" ] requestConfirmation(event) { const [el, target, source, sibling] = event.detail?.args // sibling will be undefined if dropped in last position, taking a shortcut here const areYouSure = `Are you sure you want to place ${el.dataset.name} before ${sibling.dataset.name}?` // let's suppose each