コンテンツにスキップ

データグリッド一括操作

datagrid-bulk-actions は、多数の データグリッド行に 1 つのアクションを一括実行するための公認の合成です: グリッド既存の 行選択、最初の選択行とともに現れる選択アクションバー、そして ids が ネイティブなフォームシリアライズで運ばれる一括 POST — ペイロードを 組み立てる JavaScript はどこにもありません。すでに契約を持つ部品 — datagrid-pagerinnerHTML スワップ、 confirm-action の ゲート、toastHX-Trigger — を配線し、 マークアップのバージョニングポリシーの 下で安定しています。

必要なのは installDatagrid()installDatagridActions()(どちらも 自動初期化の ./behaviors バンドルに同梱)、破壊的変種には加えて installConfirm() です。

行にチェックを入れてみてください — 一括操作バーがライブの件数とともに 現れます。ArchiveDelete はチェックされた ids と、サーバが out-of-band で再描画する隠し state 入力を POST し(サーバ自体は ステートレス)、行はトーストとともに「ページの真実」として戻ってきます。 Anvil(101)は保護されていて常に失敗するので、含めると部分失敗の warning トーストが見られます。

IDProductStatus

6 products

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>

行のチェックボックスは name="ids" value="<id>" です。htmx は非 GET リクエストで内包フォームの値を含め、未チェックのチェックボックスは 決してシリアライズされず(素の HTML の挙動)、htmx はトリガーとなった ボタンの name/value も送信します — したがって 2 行チェックした 状態で Archive をクリックすると、正確にこう POST されます:

POST /products/bulk
ids=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 にすると、ボタンに矢印キーのツールバーパターンが無料で 付いてきます。

HX-Request で分岐します。htmx への答えは常に**「ページの真実」を 載せた 200** — 再描画された行(tbody の innerHTMLdatagrid-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 はサーバ側で再検証してください(ゼロ件で隠れるバーは保証ではありません)
非 JS303 See Other + Location — 古典的な post/redirect/get。mutating-form と同じ

ステータスコードの振り付けはありません: 違うのは行とトーストだけです。 スワップインされた行は新しいチェックボックスとともに届くため、 グリッドは選択を導出し直し、全選択チェックボックスを再同期し、 再発火します — 選択のクリアとバーの非表示は構造上そうなるのです。

破壊的な一括アクションは、ボタン上の 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>

フォームは method/action を保ち、ボタンは本物の submit ボタンなので、 行をチェックしてアクションを押せば同じ ids=…&action=… ボディが ネイティブに POST され、サーバは 303 で答えます。JS の読み込み前から バーを使えるようにする必要があるなら、hidden 属性なしで出荷して ください — 選択ゼロならインストール時にビヘイビアが隠します。htmx パスの CSRF は メタ規約を 使い、ネイティブパスにはフレームワークの隠しフィールドが必要です。

選択はページ単位です — ページャーと同じ立場です。2 つのレシピを 合成するときは、現在のページを <input type="hidden" id="bulk-page" name="page" value="3"> として フォームに保ち、サーバにページャーフラグメントと並べて data-hx-swap-oob="true" で再描画させてください。ページ横断の「該当する 全 N 件を選択」、取り消し、サーバ追跡の選択は、このレシピの意図的な 非目標です。

  • バーは 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")として届きます。