ウェブUIでよく見かけるモーダルですが、Hotwireで実装したというブログ記事はよく見かけます。そして多くの場合は「JavaScriptをほとんど使わないでできた!」っていうことを誇っています。
しかしモーダルってそんな簡単なものでしょうか?私はそう思いません。 画面にモーダルを表示するのは簡単ですが、UI要素は見栄えではありません。使ってこそ価値があります。細かい動作からアクセシビリティまで、JavaScriptなしで簡単に実装できる代物ではないのです。
ここではモーダルUIの難しさをしっかり見つめて、ちゃんとしたモーダルをHotwireで作る方法を考えたいと思います。Hotwireでできる最高のものを紹介したのちに、トーンダウンした簡単なものの作り方を紹介します。
なお、ここでお見せするものは、私が普段作成するモーダルよりも随分と柔軟性が高いものです。通常はもっと簡単に実装します。いざとなったときでも対応できる最終兵器ぐらいに考えていただければと思います。
ちゃんと作ろうとするとかなり注意を要するモーダルダイアログですが、そもそも本当に必要かどうかもこの機会に再考していただければと思います。モーダルをすぐに使ってしまうデザイナーも多いのですが、安易に使うべきではないと思います。これについては、別途議論しました。
モーダルで実装する上で、以下のことを検討しておくべきかと思います。簡易的なモーダルの作り方だと、この一部は対応できますが、すべてには対応できません。
- モーダル表示のアニメーションは必要か否か:
- Bootstrapのモーダルをはじめ、モーダルの表示・非表示の際にアニメーションをつけることは一般的です。アニメーションをつけるには、HTMLから見直す必要があります
- サーバから非同期でデータ(HTML)を取得する必要があるか?:
- サーバ通信をするとレスポンス遅延を考慮する必要が出てきます
- レスポンス遅延が大きい場合を想定して、早期フィードバック(データを取得する前のモーダル表示 pending UI)を提供するべきかどうかを考える必要があります
- モーダルの中でCRUDをするか?:
- 単に「OK」「キャンセル」のボタンしかないモーダルと、サーババリデーションが必要なCRUDがあるモーダルとでは考慮するべきポイントが全然違います
- サーババリデーションがある場合、成功失敗によって動作を変えるケースが出てきます
- 例えば成功した場合の動作として、自動的にモーダルを閉じて、裏の画面も更新することが多いでしょう
- またバリデーションが失敗した場合はモーダルを開いたままにして、エラーを表示する必要があるでしょう
- 成功した時に自動的にモーダルを閉じるケースもあれば、開いたままにするケースもあるでしょう
- CreateやUpdateが成功したのちに、別のページにリダイレクトするかもしれしれません。あるいは裏のページの情報を更新するだけかもしれません

- モーダルの内容はサーバから非同期で取得します。このためにTurboを使います。
- サーバからのデータ取得は複数回行われます
- 最初にモーダルを表示するとき
- フォームを送信し、結果(成功・失敗)を受け取る時
- 初回のデータ取得は細かい制御がありませんので、Turbo Framesで十分でしょう
- フォームを送信した場合は成功・失敗(バリデーションエラー)の複数のレスポンスが想定され、それぞれで対応が変わります
- 成功した場合はトーストを表示し、元のページを再読み込みします。さらにモーダルを自動的に閉じます
- 失敗した場合は、同じページにエラー表示を重ねて表示します
- このように柔軟に結果を出し分けるために、サーバ側から制御できるTurbo Streamsを使います
- サーバレスポンスが遅い可能性を考慮し、レスポンスを待たずに、ボタンをクリックした瞬間に先にモーダルを開きます。したがってTurboリクエストと同時に、並行してStimulusでモーダルを開きます
- モーダル表示・非表示のアニメーションもStimulusで制御します
- フォームを送信して成功した場合、モーダルを自動的に閉じます。これもStimulusで制御します
- 今回はフォームが成功した時に、同じページをリロードします。しかし、他のページにリダイレクトすることも考えらます。この場合の対応も計画しておきます。
上述したことをすべて行いますので、上手の下の青色のTurboのところ、およびその下にそれを緑色のStimulusで補完するTurbo前後の2箇所をすべて行う感じになります。
なお上記の設計は、Hotwireで柔軟性をMaxにした時のものです。より単純化したケースの考え方については別途紹介します。