0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

はじめに

Ruby on Railsを使用して開発を行っていますが、モーダルウィンドウの実装で詰まりました。
備忘録として問題解決までの手順を書き記していきます。
※環境や、知識の足りていないところがあるため、おおまかな情報になります。

環境

  • Docker
  • Ruby 3.3.3
  • Rails 7.1.2
  • bootstrap 5.3.0
  • Hotwire(Turbo Stream)
  • stimulus

やりたかったこと

とあるページのログイン、新規登録処理をモーダルウィドウで行いたかった。
モーダルウィンドウ実装.gif

turbo_flame_tagを使用し、Bootstrapのモーダルを読み込む。
・バリデーションエラーをモーダルで表示するためsubmitするためのform

app/views/user_sessions/_form.html.erb
<%= turbo_frame_tag dom_id(@user, :form) do %>
  <%= render 'error' %>
  # フォームのsubmitが完了した際に発火するjavascriptを指定
  # turbo:submit-end->login_modal#close'
  <%= form_with url: login_path, html: { data: {action: 'turbo:submit-end->login_modal#close'}} do |f| %>
    <div class="w-75 mx-auto">
    <div class="mb-3">
      <%= f.label :email, t('activerecord.attributes.user.email'), class: "form-label" %>
      <%= f.email_field :email, class: "form-control", placeholder: t('placeholder.email') %>
    </div >
    <div class="mb-4">
      <%= f.label :password, t('activerecord.attributes.user.password') ,class: "form-label" %>
      <%= f.password_field :password, class: "form-control", placeholder: t('placeholder.password') %>
    </div >
    <div class="mt-4">
      <%= f.submit t('dictionary.login'), class: "btn btn-success w-100 rounded-3" %>
    </div >
    </div>
  <% end %>
<% end %>

  • <%= turbo_frame_tag dom_id(@user, :form) do %>
    →フォームを部分的に読み込ませるための目印
     
  • <%= form_with url: login_path, html: { data: {action: 'turbo:submit-end->login_modal#close'}} do |f| %>
    →フォームの送信完了後に読み込むstimulusで実装した、jsコントローラの指定。今回はapp/javascript/controllers/login_modal_controller.jsのcloseが呼び出される。
app/javascript/controllers/login_modal_controller.js
import { Controller } from "@hotwired/stimulus"
import { Modal } from "bootstrap"

// Connects to data-controller="modal"
// 画面表示時に呼び出される
export default class extends Controller {
  connect() {
    // すでに開いているモーダルを確認し・閉じる
    var registerModalElement = document.getElementById('registerModal');
    if (registerModalElement !== null ) {
      var modalInstance = Modal.getInstance(registerModalElement);
      if (modalInstance._isShown) {
        modalInstance.hide();
      } 
    }
    // モーダルを開く
    this.modal = new Modal(this.element)
    this.modal.show()
  }

  // eventがsuccessの場合にウィンドウを閉じる
  close(event) {
    if (event.detail.success) {
      this.modal.hide()
    }
  }
}

上記の設定により、バリデーションエラーが発生しても、画面が閉じられず、モーダルにエラーが表示される。

本題 turbo streamのリダイレクト処理

■問題点

モーダルを開いたままにしておくために、この処理はturbo stramを使用し、画面の一部を更新する方式をとっている。
そのため正常処理完了後にリダイレクトを行うと、更新対象がturboフレーム内になってしまい画面のリロードが行われない。

app/controllers/user_sessions_controller.rb
class UserSessionsController < ApplicationController
  skip_before_action :require_login, only: %i[new create]

  # モーダルを表示する
  def new
    @user = User.new
  end

  # ログイン処理
  def create
    @user = login(params[:email], params[:password])
    if @user
      # 正常終了
      # --------------------
      # ここの処理をどうするべきか
      # --------------------
    else
      # バリデーションエラー
      @user = User.new # エラーを保持するためのユーザーオブジェクト
      @user.errors.add(:base, 'メールアドレスまたはパスワードが無効です')
      render :new, status: :unprocessable_entity
    end
  end

  def destroy
    logout
    redirect_to root_path
  end
end

■解決方法

この問題はTurbo.visitを使用することで解消できた。
※今回の方法以外に以下の方法があった
・javascriptを使用する方法
・create.turbo_stram.erb内でリダイレクト用のリンクを作成しjavascriptでリンククリックイベントを発火させる

が、最も簡単そうな以下の方法を用いることにする。
 
1. app/javascript/application.js以下の内容を追加する。

app/javascript/application.js
Turbo.StreamActions.redirect = function () {
  Turbo.visit(this.target);
}; 

これはHotwireのTurboライブラリのStreamActionsオブジェクトに、新しいメソッドredirectを追加している。
Turbo.visitは指定されたURLにブラウザをリダイレクトさせる。
 
2. controllerを修正する

app/controllers/user_sessions_controller.rb
class UserSessionsController < ApplicationController

  # ~~~省略~~~

  def create
    @user = login(params[:email], params[:password])
    if @user
      # ▼▼▼この行を追加▼▼▼
      flash[:success] = 'ログイン完了!!'
      render turbo_stream: turbo_stream.action(:redirect, root_path)
      #  ▲▲▲この行を追加▲▲▲
    else
      @user = User.new # エラーを保持するための新しいユーザーオブジェクト
      @user.errors.add(:base, 'メールアドレスまたはパスワードが無効です') 
      render :new, status: :unprocessable_entity
    end
  end

  # ~~~省略~~~
  
end

この処理を追加することで、画面がturboフレームを抜け出し、画面がリロードされる。
またflashメッセージも画面に表示することができる。

最後に

意外と簡単な処理のみで実装が行えたが、これに問題があるかは現在わかっていない。
StreamActionsの使い方をもっと勉強しないと、すんなり実装とはいかないなと思いました。

完全な情報ではない可能性があるため、補足・指摘等ある場合はご教示ください

参考

  • turbo stimulusでのモーダルウィンドウの呼び出し

  • turbo stream からのredirect方法について

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?