Webエンジニア向けプログラミング解説動画をYouTubeで配信中!
▶ チャンネル登録はこちら

【ITニュース解説】1 Modal to Rule them All: Rails x Turbo x Stimulus

2025年09月21日に「Dev.to」が公開したITニュース「1 Modal to Rule them All: Rails x Turbo x Stimulus」について初心者にもわかりやすく解説しています。

作成日: 更新日:

ITニュース概要

Railsでモーダルを実装する際、TurboとStimulusを使い、コードの重複をなくし効率化する方法を紹介する。一つのモーダル要素にコンテンツを動的に差し込み、表示・更新・クローズを一元管理。これにより、複数の場所でモーダルを使い回し、保守性を高めることができる。

ITニュース解説

ウェブアプリケーションで一時的に表示される「モーダルウィンドウ」は、ユーザーからの入力や重要な情報の確認によく使われる機能だ。しかし、このモーダルをアプリケーションの多くの箇所で個別に実装すると、コードが重複してしまい、ウェブページの構造(DOM)が複雑になるという問題が発生しやすい。これは、開発者がコードを管理する上での手間を増やし、将来的な修正や拡張を困難にする原因となる。

この課題を解決するため、記事では一つのモーダル要素だけをアプリケーション全体で用意し、その中身を必要に応じて動的に変更するという効率的な方法を提案している。これは、ウェブサイトに表示される短い通知(フラッシュメッセージ)が、常に同じ場所を使ってメッセージを切り替えるのと似た考え方だ。このアプローチを実現するには、Railsというウェブアプリケーションフレームワークに加えて、Hotwireという技術スタック(TurboとStimulusを含む)が活用される。

このシステムを構築するには、いくつかの技術的な前提が必要となる。具体的には、最新版のRails(バージョン8以上)、Turbo Rails(バージョン2.0以上)、Stimulus Rails(バージョン1.3以上)といったRuby言語用のライブラリが必須だ。さらに、JavaScript側のパッケージとして、Stimulus本体(バージョン3.2以上)、Turbo RailsのJavaScript部分(バージョン8.0以上)、そしてモーダルの見た目を整えるためのCSSフレームワークであるBootstrap(バージョン5.3以上)も必要となる。

まずは、動的なコンテンツを表示するモーダルの基本となるHTML構造を作成する。これは通常、_modal.html.erbという部分テンプレートファイルとして用意され、アプリケーション全体で共有される。このHTMLの中には、モーダルの中心となるコンテンツ部分を特定するための<turbo_frame_tag "modal-body">という要素が配置されている。このタグは、Turboに対して「modal-body」という名前の領域が、後からサーバーからの情報で非同期的に更新される可能性があることを伝える役割を持つ。この共通モーダルは、アプリケーションの全体レイアウトファイル(例: application.html.erb)に一度だけ読み込むことで、どのページからでも利用できる準備が整う。

次に、このモーダルに表示するコンテンツをサーバーから取得するための設定を行う。Railsのルーティングファイル(routes.rb)に、モーダルコンテンツを提供する新しいURLパス(エンドポイント)を定義し、それに対応するメソッドをコントローラファイル(modal_test_controller.rb)に作成する。このコントローラメソッドは、モーダルに表示したい具体的な内容を生成する部分テンプレート(例: _modal_body.html.erb)を準備する。そして、modal.turbo_stream.erbというTurbo Streamファイルが、このコントローラメソッドから返される際に重要な役割を果たす。このファイルは、先に定義した「modal-body」という名前のTurbo Frameに対して、指定された部分テンプレートの内容で「更新」する具体的な指示をTurboに送る。

ユーザーがモーダルを開くためのボタンも作成する。このボタンは、フォームタグ(form_tag)の中に配置され、data: { turbo_frame: "modal-body", turbo_stream: true }という特別な属性が設定されている点が重要だ。この属性により、フォームが送信されると、ページ全体がリロードされることなく、指定された「modal-body」というTurbo Frameが、サーバーから返されるTurbo Streamの指示に従って非同期的に更新される、という動作をTurboが実行する。

しかし、これだけではモーダルの表示・非表示を制御できない。そこで、StimulusというJavaScriptフレームワークが登場する。modal_controller.jsというStimulusコントローラを作成し、HTML要素にdata-controller="modal"という属性を追加することで、このJavaScriptのロジックをウェブページ上のモーダル要素に紐付ける。 Stimulusコントローラは、モーダルに関連する特定のイベントを監視する。特に重要なのはturbo:before-stream-renderというイベントだ。これは、Turbo Streamがウェブページに内容を実際にレンダリングする直前に発生する。Stimulusコントローラはこのイベントを捉え、モーダルに表示するコンテンツが準備できたことを確認してから、Bootstrapのモーダル表示機能を使ってモーダルを開く。これにより、内容が空のままモーダルが表示されてしまうという、ユーザー体験を損なう状況を防ぐことができる。また、モーダルが閉じられたときに(Bootstrapのhidden.bs.modalイベント)、その内容をクリアする処理もこのコントローラで行い、次回開く際に古い情報が残らないようにする。 このコントローラには、HTMLのdata-modal-frame-id-value属性を通じて、対象となるTurbo FrameのID(例: "modal-body")が渡される。これにより、複数の異なるモーダル要素が同じStimulusコントローラを共有しながらも、それぞれ自身のコンテンツを正確に管理できるようになる。data-turbo-permanentという属性も重要な役割を果たす。これは、ページ遷移が発生してもモーダル要素がDOMから削除されずに残り続けるようにする指示であり、これによりページが移動するたびにモーダルが再生成されるのを防ぐ。

モーダルは通常、フォームの送信などの特定のアクションを実行した後、自動的に閉じられることが期待される。もしフォーム送信がページ全体のリロードを伴う場合は特に問題ないが、Turbo Streamを使ってページの一部を動的に更新するケースでは、モーダルを明示的に閉じるための処理が必要となる。 この目的のため、フォーム送信のターゲットとなるコントローラアクション(例: modal_action)を作成する。このアクションは、データ更新など本来の処理を行った後、特別なTurbo Streamを返す。このTurbo Streamは、_close_modal.turbo_stream.erbという部分テンプレートを通じて、モーダルを閉じるための「信号」を送る役割を担う。具体的には、この信号はdata-close='true'というカスタム属性を持つ目に見えない要素をモーダルの本体フレームに「追加」するという形で送られる。 Stimulusコントローラは、turbo:before-stream-renderイベントで受け取ったStreamの中にこのdata-close='true'属性を持つ要素が含まれているかをチェックする。もしそれが存在すれば、モーダルを非表示にするBootstrapのAPIを呼び出してモーダルを閉じる。これにより、フォーム送信によるページの動的な更新とモーダルの自動クローズが連携して動作する。さらに、モーダル内のアクションによってページ上の別の領域(例えば、action-change-modalというTurbo Frame)も更新されるように設定することで、ユーザーはモーダルを閉じた後もその結果をすぐに確認できる。

このアプローチの大きな利点は、その再利用性と拡張性にある。例えば、複数の場所から同じモーダルを呼び出し、それぞれで異なるコンテンツを表示したい場合、新しいモーダルHTMLを記述する必要はない。モーダルを開くボタンのフォームから送るパラメータを変更するだけで、コントローラ側で条件分岐を行い、適切なコンテンツの部分テンプレートを返すようにできる。あるいは、全く異なるコントローラやアクションから同じ共通モーダルフレームをターゲットにして、それぞれ独自のTurbo Streamを返すことも可能だ。 さらに、デザイン要件などで、既存の共通モーダルのレイアウトとは全く異なる新しいモーダルが必要になった場合でも、この仕組みは柔軟に対応できる。新しいモーダルのHTML構造を別ファイルで作成し、そこに異なるIDと、そのモーダル専用のTurbo Frame IDを設定する。この新しいモーダルも共通のStimulusコントローラを使用し、data-modal-frame-id-valueで自身のフレームIDを伝える。アプリケーションのレイアウトファイルにこの新しいモーダルも追加し、それぞれのモーダルを開くボタンを作成すればよい。フォーム送信によるモーダルを閉じる処理も、必要に応じて両方のモーダルIDに対して_close_modalパーシャルをレンダリングする形で対応できる。

このように、Rails、Turbo、Stimulusといった技術を組み合わせることで、ウェブアプリケーションにおけるモーダルウィンドウの実装を非常に効率的かつ保守性の高いものにできる。個別のモーダルごとにコードを記述する手間が省け、ウェブページの構造もシンプルに保たれるため、モダンなウェブ開発において、ユーザー体験を向上させつつ開発者の負担を軽減する強力なパターンの一つと言える。

関連コンテンツ

関連IT用語