HTML/CSS
HTMXためしてみた

うどん
公開日:2025/02/28
どうもこんにちは。
もう3月になることが信じられません。うどんです。
会社のシステムを構築する際に、Aのセレクターに連動してBのoptionの内容をかえる、といったことを定期的にしているんですが、この実装をするたびに
「jsonデータを別に用意するの、面倒だな~。DBの取得はほかでもしてるのに、このためだけにjsonデータにするの、なんかヤダ。」
とか思ってたんです。
そんなことをよく考えてる私に、「HTMX」といったものがあるということを知りました。
今回はこのHTMXを試してみます。
HTMXとは
HTMXはHTMLを拡張したJavaScriptライブラリ。
HTMLの属性を通じて、AJAX、CSS Transitions、WebSocketとServer Sent Eventsへのアクセスを提供してくれます。
つまりはデータ通信やページ更新をHTML属性で簡単に定義できるよってことです。
と、公式の記述だけだとわかりにくいのですが、ざっくりいうとHTMXではパーツごとにURLを叩いてくれて、返り値にはHTMLを記載しておくことで表示内容を切り替えるよといったものです。
HTMLで処理を実行し、JavaScriptの記述を少なくするために生まれたようなのですが、実際はどんな感じなのでしょうか。
実装する方法
公式サイト_インストールページ
https://htmx.org/docs/#installing
HTMXはCDNが用意されているので、CDNコードを設置するだけで利用することができます。
ローカルにインポートすることももちろん可能で、その場合はnpmでインストールすることができます。
実際に書いてみた
今回はバックエンドがLaravel(8)、フロントはBootstrap(5)の簡易的な環境にHTMXを追加してみました。
実際のソースを貼っていきます。
routeはこんな感じの内容にしていました。
1 2 3 4 5 6 7 |
// HTMXテスト用のビュー Route::get('/htmx', [HtmxController::class, 'getHtmx']); Route::get('/message', [HtmxController::class, 'getMessage']); Route::get('/submit', [HtmxController::class, 'submitForm']); Route::post('/submit', [HtmxController::class, 'submitForm']); Route::get('/time', [HtmxController::class, 'getTime']); |
Controller
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 |
class HtmxController extends Controller { // ページ表示 public function getHtmx() { return view('htmx'); } // メッセージを返す public function getMessage() { $quotes = [ "成功は最終的なものではなく、失敗は致命的なものでもない。大切なのは続ける勇気である。 - ウィンストン・チャーチル", "変化を恐れるな。過去の失敗を恐れるな。 - コリン・パウエル", "何事も達成するための唯一の方法は、まず始めることである。 - マーク・トウェイン", "成功は準備と機会が出会うところにある。 - ボビー・アンセルム", "あなたの時間は限られています。他人の人生を生きて浪費してはいけません。 - スティーブ・ジョブズ", ]; // 配列からランダムに名言を選択 $message = $quotes[array_rand($quotes)]; return response($message); } // フォームデータを受け取り、応答する public function submitForm(TestRequest $request) { if($request->name) { $name = $request->name; return view('response', compact('name')); } return view('submit'); } // 現在時刻を返す public function getTime() { $now = Carbon::now()->format('Y-m-d H:i:s'); return response($now); } } |
Views
ベースになるhtmx.blade.php
※今回はCDNでインストールしてみました。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 |
<!DOCTYPE html> <html lang="ja"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>HTMX x Laravel x Bootstrap</title> <!-- Bootstrap 5 CSS --> <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0-alpha1/dist/css/bootstrap.min.css" rel="stylesheet"> <!-- HTMX --> <script src="https://unpkg.com/htmx.org@2.0.4" integrity="sha384-HGfztofotfshcF7+8n44JQL2oJmowVChPTg48S+jvZoztPfvwD79OC/LTtG6dMp+" crossorigin="anonymous"></script> </head> <body> <div class="container my-5"> <h1 class="mb-4 text-center">HTMX x Laravel x Bootstrap</h1> <!-- 1. ボタンでサーバーからメッセージを取得 --> <div class="mb-5"> <h2>1. ボタンでサーバーからメッセージを取得</h2> <button class="btn btn-primary" hx-get="{{ url('/message') }}" hx-target="#output" hx-swap="innerHTML"> メッセージを取得 </button> <div id="output" class="mt-3 alert alert-info">ここにメッセージが表示されます。</div> </div> <hr> @include('submit') <!-- 3. タイマーでリアルタイム更新 --> <div> <h2>3. タイマーでリアルタイム更新</h2> <h3>現在の時刻:</h3> <div hx-get="{{ url('/time') }}" hx-trigger="every 1s" hx-swap="innerHTML" class="alert alert-warning"> 読み込み中... </div> </div> </div> <!-- Bootstrap 5 JS --> <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0-alpha1/dist/js/bootstrap.bundle.min.js"></script> </body> </html> |
submit.blade.php
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
<!-- 2. 簡単なフォーム送信 --> <div class="mb-5"> <h2>2. 簡単なフォーム送信</h2> <form hx-post="{{ url('/submit') }}" hx-target="#form-result" hx-swap="outerHTML" hx-headers='{"X-CSRF-TOKEN": "{{ csrf_token() }}"}' class="form-inline" > <div class="mb-3"> <label for="name" class="form-label">名前:</label> <input type="text" id="name" name="name" class="form-control mx-2" placeholder="名前を入力" value="{{ $name ?? '' }}"> </div> <button class="btn btn-success">送信</button> </form> @include('response') </div> <hr> |
response.blade.php
1 2 3 4 5 6 7 8 9 |
@if($errors->has('name')) <div id="form-result" class="mt-3 alert alert-danger">{{ $errors->first('name') }}</div> @elseif(isset($name)) <div id="form-result" class="mt-3 alert alert-success"> <strong>{{ $name ?? '' }}</strong> さん、こんにちは!送信ありがとうございます✨ </div> @else <div id="form-result" class="mt-3 alert alert-info">ここに結果が表示されます。</div> @endif |
HTMLを触ってみて思った使えそうな場面
フォーム入力時に部分的に表示を変えたくても、サーバー側で処理を行うと基本は全画面描写をし直すことになっていたかと思うのですが、HTMXの場合は部分的な要素を差し替えられるため、今までjsonレスポンスを返していた部分では使うこともできるのではないかと思いました。
jsonと違っていいことは、バリデーションを通してから検索できることかと思います。
ただこのバリデーションをしようとした際に、ちょっとした苦労がありました。
HTMLのちょっぴり苦労した点
基本的には指定されたアクションをトリガーにHTMLを更新するのがHTMXの機能であるため、バリデーションを使った場合にエラー表示があってもLaravel側にリダイレクト「しない」という設計にする必要があったことでした。
また、バリデーションを通過できたあともviewファイルを返却するのではない場合は記述を変える必要があります。
そうしておかないとHTMLを入れ替えて表示させるHTMXでは無限おなじ内容を入れ子に表示させてしまうのです・・・。
そのため、リダイレクトさせないようにするために、TestRequest.phpを作りました。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 |
<?php namespace App\Http\Requests; use Illuminate\Contracts\Validation\Validator; use Illuminate\Foundation\Http\FormRequest; use Illuminate\Http\Exceptions\HttpResponseException; class TestRequest extends FormRequest { /** * Determine if the user is authorized to make this request. * * @return bool */ public function authorize() { // falseからtrueに変更する return true; } /** * Get the validation rules that apply to the request. * * @return array */ public function rules() { return [ 'name' => 'required|max:5', ]; } public function messages() { return [ '*.required' => ':attributeは必須項目です', '*.max' => ':max桁以下で入力してください', ]; } // バリデーションに失敗した場合は、リダイレクトせず view を返す protected function failedValidation(Validator $validator) { $view = ''; $data = []; if($this->isMethod('post')) { $view = 'response'; $data = [ 'name' => $this->name, 'errors' => $validator->errors(), ]; } else if($this->isMethod('put')) { $view = 'response'; $data = [ 'name' => $this->name, 'errors' => $validator->errors(), ]; } $response = response()->view($view, $data); throw new HttpResponseException($response); } public function attributes() { return [ 'name' => 'お名前', ]; } } |
このfailedValidationの中で、エラー時はどのviewファイルに返すかということを設定しています。
HTMXを使ってみた感想まとめ
新しいものは調べてみてもわからないことだらけでしたが、HTMXは「HTMLのレスポンスを返却する」という機能であることを想定すると、ほかのライブラリなどと比べると原因もわかりやすい気がしました。
また、トリガーフックも細かく設定できるので、もっとちゃんと調査すれば、さらに使える場面が増えそうなライブラリだとも感じました。
もう少し調査を進めて、実際のシステムにも取り入れようと思います。