EY-Office ブログ

JS嫌いの人はhtmxを使うと良いのでは

最近htmxが話題になっているようなので、とりあえず触ってみました。

htmxホームページには、

<script src="https://unpkg.com/htmx.org@1.9.10"></script>
<!-- have a button POST a click via AJAX -->
<button hx-post="/clicked" hx-swap="outerHTML">
  Click Me
</button>

これは

ユーザーがこのボタンをクリックしたら、/clickedにAJAXリクエストを発行し、ボタン全体をHTMLレスポンスに置き換える。

と解説されています。HTMLタグの属性を拡張し、サーバーへのリクエスト等が簡単に書けるJavaScriptライブラリーです。
ドキュメントを見ても、通常のJavaScriptの構文ほぼ出てきません❗ HTMLと同じように宣言的に書けます❗

htmx Bing Image Creatorが生成した画像を使っています

Ruby on Railsでhtmxを使ってみた

いつものジャンケンアプリをhtmxを作って行こうと思います。

Ruby on Railsのみ

バックエンドを含め、まずはRuby on Railsで基本機能を作ってみましょう

app/controllers/jyankens_controller.rb

コントローラーは以下のようになります、htmx用のメソッドの説明は後で行います。

  • ① データベースのjyankensテーブルから対戦記録を取得し表示します
  • ② ジャンケンのボタンを押したさいには、createアクションが実行されます
  • ③ 次の事を考えジャンケンの処理をjyanekn_ponメソッドに書きました
    • コンピューターの手は乱数で発生
    • 勝敗を計算
    • jyankensテーブルに結果を追加
class JyankensController < ApplicationController

  def index                                    # ← ①
    @jyankens = Jyanken.order(created_at: :desc).all
  end

  def list                                     # ← ⑩
    index
    render partial: 'list'
  end

  def htmx                                     # ← ⑪
  end

  def create                                   # ← ②
    jyanekn_pon
    redirect_to jyankens_url
   end

   def pon                                     # ← ⑫
    jyanekn_pon
    redirect_to list_jyankens_url
  end

  private
    def jyanekn_pon                            # ← ③
      @jyanken = Jyanken.new(jyanken_params)
      @jyanken.computer = rand(3)
      @jyanken.judgment = (@jyanken.computer - @jyanken.human) % 3

      @jyanken.save
    end

    def jyanken_params
      params.require(:jyanken).permit(:human)
    end
end
app/views/jyankens/index.html.erb
  • indexページに対戦記録と、ジャンケンボタンを置きました
  • 対戦記録表示は_listページに切り出しました
<p style="color: green"><%= notice %></p>

<h1>ジャンケン</h1>

<%= button_to "グー", jyankens_path(jyanken: {human: 0}) %>
<%= button_to "チョキ", jyankens_path(jyanken: {human: 1}) %>
<%= button_to "パー", jyankens_path(jyanken: {human: 2}) %>

<%= render "list" %>
app/views/jyankens/_list.html.erb
  • 対戦記録の表示
<table>
  <thead>
    <tr>
      <th>人間</th>
      <th>コンピューター</th>
      <th>勝敗</th>
    </tr>
  </thead>
  <tbody>
    <% @jyankens.each do |jyanken| %>
      <tr>
        <td><%= te_s(jyanken.human) %></td>
        <td><%= te_s(jyanken.computer) %></td>
        <td><%= judge_s(jyanken.judgment) %></td>
      </tr>
    <% end %>
  </tbody>
</table>
app/helpers/jyankens_helper.rb
  • ジャンケンの手や勝敗の文字列表示ヘルパー
module JyankensHelper
  def te_s(te)
    ["グー", "チョキ", "パー"][te]
  end

  def judge_s(juge)
    ["引き分け", "勝ち", "負け"][juge]
  end
end

htmx

いよいよhtmxですが、その前にhtmxから呼び出されるアクションを作っておきます。

  • config/routes.rb
Rails.application.routes.draw do
  resources :jyankens do
    get 'htmx', on: :collection
    get 'list', on: :collection
    post 'pon', on: :collection
  end
end

さきほどは説明しなかったコントローラーの説明

  • ⑩ 対戦記録の部分のみ返すlistアクション
  • ⑪ htmx用ページの表示アクション
  • ⑫ ジャンケン対戦アクション
    • 対戦データの作成後は、listアクションにリダイレクトされ実行されます
app/views/jyankens/htmx.html.erb

いよいよhtmxです😃

一見して気がつくように、JavaScript言語は書かれていません(もちろんhtmxはJavaScriptで実現されているのですが)。

  • ① htmxのライブラリーを読み込んでいます
  • ② RailsではCSRF攻撃への対処用のトークンが必要になるので、ここで準備しています
  • ③ ジャンケン・ボタンの記述
    • hx-postでこのボタンを押したときに、POSTリクエストするバックエンドURLの指定
      • ここではジャンケンの手をURLのqueryで与えています
    • hx-includeは、POSTされる付加データを指定しています、ここでは②のトークンをジャンケンの手に付加しています
    • hx-targetは、このリクエストに対するバックエンドからのレスポンス(対戦記録のHTML)を書き込むエリアをCSSセレククターで指定しています
  • ④ 対戦記録が書き込まれるエリア
  • ⑤ これは特殊なイベントを指定しています
    • hx-getでこのイベントが発生した時にGETリクエストをするバックエンドURLの指定
    • hx-triggerイベントの種類を指定します。load指定は、このページがブラウザーに読み込まれた時にイベントが発生します
    • hx-target ③で説明したのと同じです
<script src="https://unpkg.com/htmx.org@1.9.10/dist/htmx.js">
</script>                                                      <!-- ← ① -->

<h1>ジャンケン</h1>

<input type="hidden" name="authenticity_token"
  value="<%= form_authenticity_token %>" />            <!-- ← ② -->
                                                       <!-- ↓ ③ -->
<button hx-post="/jyankens/pon?jyanken[human]=0"
  hx-include="[name='authenticity_token']" hx-target="#list">グー</button>
<button hx-post="/jyankens/pon?jyanken[human]=1"
  hx-include="[name='authenticity_token']" hx-target="#list">チョキ</button>
<button hx-post="/jyankens/pon?jyanken[human]=2"
  hx-include="[name='authenticity_token']" hx-target="#list">パー</button>

<div id="list"></div>                                 <!-- ← ④ -->
                                                      <!-- ↓ ⑤ -->
<div hx-get="/jyankens/list" hx-trigger="load" hx-target="#list">

jQuery

説明は省略しますが、上のhtmxと同じ事をjQueryで実現すると以下のようになります。htmxの動作が想像できるJavaScriptのコードになっていると思います。

<script src="https://code.jquery.com/jquery-3.7.1.min.js"
  integrity="sha256-/JqT3SQfawRcv/BIHPThkBvs0OEvtFFmqPF/lYI/Cxo="
   crossorigin="anonymous"></script>

<script>
$(document).ready(function() {
  $.get("/jyankens/list", success)
});

function pon(te) {
  const data = {
    jyanken: {human: te},
    authenticity_token: "<%= form_authenticity_token %>"
  };
  $.post("/jyankens/pon", data, success);
}

function success(data) {
  console.log(data);
  $("#list").html(data);
}
</script>

<h1>ジャンケン</h1>

<button onClick="pon(0)">グー</button>
<button onClick="pon(1)">チョキ</button>
<button onClick="pon(2)">パー</button>

<div id="list"></div>

まとめ

htmxは従来jQuery等で書いてきたコードのうち良くあるAjax等のコードをJavaScript言語を使わずにHTMLタグの拡張で置き換えてくれるライブラリーです。
世の中には、JavaScriptの嫌いなWeb系プログラマーもいると思います、そのような方はhtmxを使うと良いのではないでしょうか?

最後にhtmxホームページのEssaysにあるミームが興味深いので、ぜひ眺めてみてください。

- about -

EY-Office代表取締役
・プログラマー
吉田裕美の
開発者向けブログ