データグリッド一括操作
datagrid-bulk-actions は、多数の
データグリッド行に
1 つのアクションを一括実行するための公認の合成です: グリッド既存の
行選択、最初の選択行とともに現れる選択アクションバー、そして ids が
ネイティブなフォームシリアライズで運ばれる一括 POST — ペイロードを
組み立てる JavaScript はどこにもありません。すでに契約を持つ部品 —
datagrid-pager の
innerHTML スワップ、
confirm-action の
ゲート、toast の
HX-Trigger — を配線し、
マークアップのバージョニングポリシーの
下で安定しています。
必要なのは installDatagrid() と installDatagridActions()(どちらも
自動初期化の
./behaviors
バンドルに同梱)、破壊的変種には加えて installConfirm() です。
行にチェックを入れてみてください — 一括操作バーがライブの件数とともに
現れます。Archive と Delete はチェックされた ids と、サーバが
out-of-band で再描画する隠し state 入力を POST し(サーバ自体は
ステートレス)、行はトーストとともに「ページの真実」として戻ってきます。
Anvil(101)は保護されていて常に失敗するので、含めると部分失敗の
warning トーストが見られます。
マークアップ
Section titled “マークアップ”1 つの <form> がグリッドとバーを包みます:
<form method="post" action="/products/bulk"> <div class="hc-toolbar" role="toolbar" aria-label="Bulk actions" data-hc-datagrid-actions="#grid" hidden> <span data-hc-datagrid-count></span> <button class="hc-button" type="submit" name="action" value="archive" data-hx-post="/products/bulk" data-hx-target="#rows" data-hx-swap="innerHTML" data-hx-disabled-elt="this">Archive</button> </div>
<div class="hc-datagrid" id="grid"> <div class="hc-datagrid__scroll"> <table class="hc-datagrid__table"> <thead class="hc-datagrid__head"> <tr> <th class="hc-datagrid__headcell"> <input type="checkbox" class="hc-checkbox" aria-label="Select all"> </th> <th class="hc-datagrid__headcell" scope="col">Product</th> </tr> </thead> <tbody class="hc-datagrid__body" id="rows"> <tr class="hc-datagrid__row"> <td class="hc-datagrid__cell"> <input type="checkbox" class="hc-checkbox" name="ids" value="101" aria-label="Select Anvil"> </td> <td class="hc-datagrid__cell">Anvil</td> </tr> </tbody> </table> </div> </div></form>ids がどう運ばれるか
Section titled “ids がどう運ばれるか”行のチェックボックスは name="ids" value="<id>" です。htmx は非 GET
リクエストで内包フォームの値を含め、未チェックのチェックボックスは
決してシリアライズされず(素の HTML の挙動)、htmx はトリガーとなった
ボタンの name/value も送信します — したがって 2 行チェックした
状態で Archive をクリックすると、正確にこう POST されます:
POST /products/bulkids=101&ids=102&action=archiveペイロードを正直に保つ 2 つのルール:
- 全選択チェックボックスに
nameを付けない — これは UI 上の 便宜であり、リクエストへ漏れてはいけません。 - 各アクションボタンは
type="submit" name="action" value="<verb>"— JS がオフでも同じマークアップが(同じボディで)ネイティブに POST します。1 つのエンドポイントでactionにより分岐します。アクション 別 URL でも構いません。
これはレシピの土台なので、実 htmx に対して
test-browser/datagrid-bulk-actions.spec.mjs で固定されています —
htmx がクリックを処理する間、ネイティブ送信が抑止されたままである
ことも含めて。
installDatagridActions() は、グリッドの選択を
data-hc-datagrid-actions="<grid selector>" を持つ任意の要素へミラー
します:
[data-hc-datagrid-count]の子は i18n キーdatagrid.selected(デフォルト{selected} selected。上書きには{total}パラメータも 利用可)をデフォルトのrole="status"つきで描画し、- 何も選択されていない間、バーは
hiddenです。
これはグリッドの公開イベント hc:datagridselectionchange に駆動され
ます — 行スワップ後のグリッドの再発火も含みます。一括操作の後にバーが
クリアされるのはそれによってです。バーを role="toolbar" の
hc-toolbar にすると、ボタンに矢印キーのツールバーパターンが無料で
付いてきます。
サーバレスポンス契約
Section titled “サーバレスポンス契約”HX-Request で分岐します。htmx への答えは常に**「ページの真実」を
載せた 200** — 再描画された行(tbody の innerHTML、
datagrid-pager と
同じ形)、任意の out-of-band フラグメント、そしてトーストです:
| リクエスト | レスポンス |
|---|---|
| htmx、成功 | 200 — 再描画された行 + OOB のステータス / ページャー + HX-Trigger: {"hc:toast":{"message":"3 archived","variant":"success"}} |
| htmx、部分失敗 | 同じ 200 の形 — 行は実際に起きたことを示し、トーストは variant: "warning"("3 archived, 1 failed") |
htmx、空 / 古い ids | 同じ 200 の形 — no-op の行、トーストは variant: "info"。ids はサーバ側で再検証してください(ゼロ件で隠れるバーは保証ではありません) |
| 非 JS | 303 See Other + Location — 古典的な post/redirect/get。mutating-form と同じ |
ステータスコードの振り付けはありません: 違うのは行とトーストだけです。 スワップインされた行は新しいチェックボックスとともに届くため、 グリッドは選択を導出し直し、全選択チェックボックスを再同期し、 再発火します — 選択のクリアとバーの非表示は構造上そうなるのです。
確認つき破壊的変種
Section titled “確認つき破壊的変種”破壊的な一括アクションは、ボタン上の confirm-action パターンでゲートします — 他はすべてそのままです:
<button class="hc-button" data-variant="error" type="submit" name="action" value="delete" data-hc-confirm="Delete the selected products? This cannot be undone." data-hx-trigger="hc:confirmed" data-hx-post="/products/bulk" data-hx-target="#rows" data-hx-swap="innerHTML" data-hx-disabled-elt="this">Delete</button>非 JS デグラデーション
Section titled “非 JS デグラデーション”フォームは method/action を保ち、ボタンは本物の submit ボタンなので、
行をチェックしてアクションを押せば同じ ids=…&action=… ボディが
ネイティブに POST され、サーバは 303 で答えます。JS の読み込み前から
バーを使えるようにする必要があるなら、hidden 属性なしで出荷して
ください — 選択ゼロならインストール時にビヘイビアが隠します。htmx
パスの CSRF は
メタ規約を
使い、ネイティブパスにはフレームワークの隠しフィールドが必要です。
スコープと合成
Section titled “スコープと合成”選択はページ単位です — ページャーと同じ立場です。2 つのレシピを
合成するときは、現在のページを
<input type="hidden" id="bulk-page" name="page" value="3"> として
フォームに保ち、サーバにページャーフラグメントと並べて
data-hx-swap-oob="true" で再描画させてください。ページ横断の「該当する
全 N 件を選択」、取り消し、サーバ追跡の選択は、このレシピの意図的な
非目標です。
アクセシビリティ
Section titled “アクセシビリティ”- バーは
role="toolbar"とaria-labelを持つhc-toolbarです。installToolbar()がボタンに矢印キーの roving tabindex パターンを 与えます。 [data-hc-datagrid-count]要素にはinstallDatagridActions()が デフォルトのrole="status"を与えるため、選択数の変化は追加の マークアップなしで丁寧に読み上げられます。- 行チェックボックスには行ごとの
aria-label("Select Anvil")を、 全選択にはそれ自身のラベル("Select all")を与えてください。 全選択の中間状態はネイティブの:indeterminateで、スワップの たびに再同期されます。 - リクエスト中のアクションボタンはネイティブの
disabled(data-hx-disabled-elt="this")になり、支援技術がそれを報告 します。結果はトースト (role="status"、エラーはrole="alert")として届きます。
- データグリッド — 行選択、選択アクションバーのセクション。
- データグリッドのページネーションレシピ —
innerHTMLスワップのルールと OOB フラグメント。 - 確認アクションレシピ — 破壊的アクションのゲート。
- トーストレシピ —
HX-Triggerによる結果フィードバック。 - ミューテーションフォームレシピ — 共有する
HX-Request分岐。