🦓

[Rails]turboとstimulusによるモーダル

2023/08/08に公開

はじめに

turbo-frameを使ってモダールを実装してみます。

ライブラリを使った記事はこちら

環境

Rails 7.0.4.3
ruby 3.2.1

turbo_frame_tagとは

turbo_frame_tagは、Hotwire フレームワークの一部であり、Turbo Streams の一部として提供されているタグです。Turbo Streams は、非同期で部分的なページ更新を実現するためのツールセットで、turbo_frame_tagはその中で特定の領域を更新するために使用されます。

turbo_frame_tagは、指定された HTML 要素をフレームとしてマークアップするために使用されます。これにより、そのフレーム内のコンテンツが非同期に更新されることができます。

よくある使用シーン:

  1. 部分的なコンテンツの更新: ページ全体をリロードせずに特定の部分的なコンテンツを更新する場合に使用します。例えば、コメントセクションや通知リストの更新などがこれに該当します。

  2. フォームの非同期送信: フォームの送信時にページ全体をリロードする代わりに、フォーム内の特定の部分だけを更新したい場合に使用します。

  3. モーダルの内容の更新: モーダルウィンドウ内のコンテンツを非同期で更新する場合に便利です。

レイアウトにturbo_frame_tagを追加する

全ページ上にモーダルを開けるようにしたいので、application.html.erbmodalフレームを追加します。

app/views/layouts/application.html.erb
<%= turbo_frame_tag 'modal' %>

turbo-frameタグを追加されました。

turbo_frame_tagにモーダルコンテンツを追加する

turbo_frame_tag内にモダールコンテンツにを追加していきます。

app/views/posts/new.html.erb
<%= turbo_frame_tag 'modal' %>
 # コンテンツ
<% end %>

キャンセルボタンもしくは閉じるボタンも入れましょう。
クリックしたらmodalsコントローラーのcloseアクションを発火させ、モーダルを閉じるようになります。

<%= link_to 'キャンセル', '#', data: {
  controller: "modals",
  action: 'modals#click->close'
} %>

モーダルボタンを作成する

リンクをクリックしたらモーダルフォームを表示させます。

app/views/shared/_header.html.erb
<%= link_to '投稿する',
            new_blog_path,
            data: { turbo_frame: 'modal' },
%>

turbo-frameタグにURLを追加されました。

stimulusコントローラーを作成する

bin/rails g stimulus modals
      create  app/javascript/controllers/modals_controller.js

モーダルにhiddenクラスを追加しページ上に非表示させます。

app/javascript/controllers/modals_controller.js
import { Controller } from "@hotwired/stimulus"

export default class extends Controller {
    static targets = ['modal']

    connect(){

    }
    close(e){
        e.preventDefault();
        const modal = document.getElementById("modal");              
        modal.classList.add("hidden");
    }
}

ページをリロードさせたいとき

モーダルからフォームの送信の処理を行う場合、送信後にid="modal"の要素がないのでcontent-missingのエラーがでます。
そういう時にページをリロードさせたいですね。
対応方法としては、リスポンスからもう一度ページにアクセスさせます。
リスポンス->content-missing->visit(response.url);

document.addEventListener("turbo:frame-missing", (event) => {
      const { detail: { response, visit } } = event;
      event.preventDefault();
      visit(response.url);
});

https://stackoverflow.com/questions/75701014/using-turbo-frames-how-can-i-reload-the-whole-page-if-the-form-submited-succeede/75704489#75704489

終わりに

こちらの方法、ページをリロードせずに新しいコンテンツを表示させたい時にはすごい便利だと感じました。
https://turbo.hotwired.dev/handbook/frames

Discussion