📓 memotty

【htmx】行を選択して一括更新

参考 #

実際のコード #

<form id="checked-contacts"
      hx-post="/users"
      hx-swap="innerHTML settle:3s"
      hx-target="#toast">
    <table>
      <thead>
      <tr>
        <th>Name</th>
        <th>Email</th>
        <th>Active</th>
      </tr>
      </thead>
      <tbody id="tbody">
        <tr>
          <td>Joe Smith</td>
          <td>joe@smith.org</td>
          <td><input type="checkbox" name="active:joe@smith.org"></td>
        </tr>
        ...
      </tbody>
    </table>
    <input type="submit" value="Bulk Update" class="btn primary">
    <output id="toast"></output>
</form>

公式htmxの サンプルコードです。

概要 #

チェックボックスで選択されたユーザーを/users エンドポイントに POST で送信し、結果メッセージを #toast に表示するというバルク更新。

解説 #

formタグ #

<form id="checked-contacts"
      hx-post="/users"
      hx-swap="innerHTML settle:3s"
      hx-target="#toast">
  • hx-post="/users" : HTML form送信ではなく、 htmxで /usersPOST リクエストする
  • hx-swap="innerHTML settle:3s: SSRしたHTMLを hx-targetで指定した要素の innerHTMLに置き換える。
    • また、描画が安定(settle)してから3秒間はそのまま表示する(アニメーションなどの完了待ちに使ったり)
  • hx-target="#toast: レスポンス内容を描画する要素を指定(id="toast")

input active #

<tbody id="tbody">
    <tr>
      <td>Joe Smith</td>
      <td>joe@smith.org</td>
      <td><input type="checkbox" name="active:joe@smith.org"></td>
    </tr>
    ...
  </tbody>
  • name="active:joe@smith.org"
    • チェックされている要素のみフォーム送信される
    • active:joe@smith.org=onのような形式でサーバーに送信される
  • tbodyに複数行存在することが前提

submit #

<input type="submit" value="Bulk Update" class="btn primary">
<output id="toast"></output>
  • submitを押すと hx-postが発火する
  • SSR結果は <output id="toast"></output> へレンダリングされる

サーバー側の処理 #

リクエストボディ #

active:joe@smith.org=on
active:bob@tanaka.jp=on

Goのコード #

func usersHandler(w http.ResponseWriter, r *http.Request) {
    r.ParseForm()

    var activeEmails []string
    for key := range r.PostForm {
        if strings.HasPrefix(key, "active:") {
            email := strings.TrimPrefix(key, "active:")
            activeEmails = append(activeEmails, email)
        }
    }

    fmt.Fprintf(w, "%d users updated successfully", len(activeEmails))
}

上記handlerを設定したルーティングへリクエストを送ると、2 users updasted successfully がレスポンスとして返り<output id="toast"></output> 内へ挿入される。