コンテンツにスキップ

データグリッドの編集エラー

データグリッドの インラインコミットは楽観的です — このレシピは、サーバーにしか 判定できない値のためにそのフィードバックループを閉じます。各行は 永続化ワイヤリングを運ぶレコード <tbody> で、サーバーの応答は そのレコードを再描画します: 200 は行だけで確定、422 は行 (サーバーの値に復元、セルは data-invalid) .hc-datagrid__error-row のメッセージを一緒に持ってきます — 1 つの アトミックなスワップ単位なので、古いエラーが取り残されることは ありません。 バージョニングポリシーの 対象です。

Price セルをダブルクリック(またはフォーカスして Enter)し、abc をコミットしてください — セルは保存中 状態を見せた後、エラーリングつきでサーバーの値に戻り、メッセージが 拒否された入力を名指しします。0 より大きい数値をコミットすると 200 ブランチが確定してクリアします。

NamePriceShip date
Chai18.002026-08-01
Chang19.002026-08-03
<div class="hc-datagrid" id="items-grid" data-hc-datagrid-pending>
<template data-datagrid-editor data-col="price">
<input class="hc-input" type="text" inputmode="decimal" aria-label="Price">
</template>
<div class="hc-datagrid__scroll">
<table class="hc-datagrid__table">
<thead class="hc-datagrid__head"></thead>
<tbody class="hc-datagrid__record" id="item-1"
data-hx-patch="/items/1"
data-hx-trigger="hc:datagridedit"
data-hx-vals="js:{ col: event.detail.col, value: event.detail.value }"
data-hx-disinherit="hx-vals"
data-hx-swap="outerHTML">
<tr class="hc-datagrid__row">
<td class="hc-datagrid__cell">Chai</td>
<td class="hc-datagrid__cell" data-numeric data-editable
data-col="price" data-value="18">18.00</td>
</tr>
</tbody>
</table>
</div>
</div>

data-hc-datagrid-pending はグリッドを保存中状態にオプトインします (再描画が届くまでセルに data-pending + aria-busy)。422 の スワップは一度だけグローバルに許可します — field-errors の 許可スニペット:

document.body.addEventListener('htmx:beforeSwap', (event) => {
if (event.detail.xhr.status === 422) {
event.detail.shouldSwap = true;
event.detail.isError = false;
}
});

PATCH /items/:id(colvalue):

ケースレスポンス
受理200 + レコード <tbody> — 行のみ。セルは値のサーバーによる整形を表示。楽観的コミットを確定し、data-pending をクリアし、以前のエラー行があればアトミックに取り除きます
拒否422 + レコード <tbody> — セルはサーバーの現在値に戻り、data-invalid + aria-invalid + aria-describedby 付き。続けて role="alert" のメッセージが拒否された入力を名指しする __error-row
確認が必要200 + レコード <tbody>確認待ち状態で — 下記参照。まだ何もコミットされていません
未知の行 / 列404 — 何もスワップしない。標準のエラートーストがカバー

キャンセルにはもう 1 つの経路が要ります: GET /items/:id → 保存済みの レコードをそのまま返します。何も書き込んでいないので、取り消すものは ありません。

レコード tbody がアトムです: 行とエラー行は常に一緒に移動します — OOB の帳簿づけなし、ステートレスなサーバー、そしてセルがサーバーの 保証しない値を表示することはありません。

値としては受理可能だが異常なものがあります: 未来日の出荷日、 規定を超える割引、通常の 10 倍の数量。これらはエラーではありません — 422 は「直す必要のないものを直せ」と言うことになります — そして ルールを知っているのはサーバーだけなので、クライアント側の確認 (installConfirm)では表現できません。ルールは送信した後で、 ユーザーが既に確定した後に判明するからです。

正直な答えは 200 です。何も失敗しておらず、何も拒否されていません。 サーバーは会話を続けているだけです。htmx:beforeSwap の許可も不要に なります。

<td class="hc-datagrid__cell" data-editable data-col="ship"
data-value="2027-01-01" data-attention="warning"
aria-describedby="item-2-note">2027-01-01</td>
<tr class="hc-datagrid__error-row">
<td class="hc-datagrid__error" data-tone="warning" colspan="3">
<span role="alert" id="item-2-note">2027-01-01 は未来日です。この日付で Chang を出荷する場合は確認してください。</span>
<button class="hc-button" data-size="sm" data-variant="primary" type="button"
data-hx-patch="/items/2"
data-hx-vals='{"col":"ship","value":"2027-01-01","confirm":"9f2c1a"}'
data-hx-target="closest tbody" data-hx-swap="outerHTML">確認して更新</button>
<button class="hc-button" data-size="sm" type="button"
data-hx-get="/items/2"
data-hx-target="closest tbody" data-hx-swap="outerHTML">取り消す</button>
</td>
</tr>

セルは提案値を表示します — 見えないものは確認できません — そして data-attention="warning" でマークされます。レコードにも同じ属性を 付けるので、上にどのティントが塗られても行は「対応が必要」と読めます。

トークンは値に束縛します。confirm は 1 つの (行・列・値) — バージョン管理されたストアではバージョンも含めて — に対して発行される単回限りのトークンです。この束縛がないと、ある値に 対して得た確認が別の値をコミットしてしまい、409 のバージョンガードも リプレイで回避されます。confirm=1 だけを見るサーバーは、確認ではなく 混乱した代理人を作ったことになります。ボタンは 静的な data-hx-vals(js: ではない)を使うので、確認対象の値は描画時に 固定され、CSP にも安全です。

一括操作は既に同じ形を持っています: datagrid-bulk-errors のプリフライト(「18 件は実行可能、2 件は不可」)は、多数の行に対する 同じ会話です。

プログレッシブエンハンスメント

Section titled “プログレッシブエンハンスメント”

インラインセル編集自体が JavaScript のエンハンスメントです。no-JS の 経路は inline-edit レシピのページレベルフォームです。グリッドはスクリプトなしでも描画・ 閲覧できます。

  • メッセージは内側要素の role="alert" で読み上げられます(セルは gridcell ロールを保持)。拒否されたセルは aria-invalid + aria-describedby でリンクされます。
  • 保存中は 1 セルの aria-busy だけ — グリッドの残りは操作可能な まま。エラー行はキーボードナビゲーションに入りません。
  • フォーカスはスワップを生き延び、Enter でその場で 再編集できます。
  • 確認行はナビゲーション行列の外にあるため、中のボタンは通常のタブ順を 保ちます。role="alert" はフォーカスを奪わずに問いを読み上げます。
  • データグリッド — このレシピが駆動する編集ライフサイクル状態(data-pendingdata-invalid、エラー行スロット)。
  • field-errors — フォーム全体のための同じ 422 哲学。
  • inline-edit — 単一フィールド click-to-edit の兄弟。