🦓

[Rails7]turboでフラッシュメッセージを表示させる

2023/08/26に公開

はじめに

初めてRails7とturboを導入してアプリを作る時にフラッシュメッセージの表示で躓いたのでまとめてみました。

環境

Rails 7.0.7
ruby 3.2.1

scaffoldで作ったTodoのサンプルアプリで説明します。

bin/rails g scaffold Todo title:string

フラッシュメッセージが表示されない

Rails 7では、デフォルトでフォームの処理がTURBO_STREAMに統合されています。

Processing by TasksController#destroy as TURBO_STREAM

フォームがTurbo Streamsを使用して非同期で送信されるます。
送信後、適切なTurbo Streamsの更新を行うことで、フォームの結果をページ内で更新されます。

通常のHTMLレスポンスでは、サーバーから返されたHTMLコードにフラッシュメッセージが含まれており、ブラウザがそれを表示します。
一方、Turbo Streamsは非同期的にコンテンツを更新するため、更新内にフラッシュメッセージが含まれないです。

一番簡単の解決方法としては、フォーム内に{ turbo: false }を追加し、turboを無効にすることですが、turboを使う意味無くなってしまうので他の案を考えます。

コントローラーのturbo_streamブロック内フラッシュメッセージを追加する

ページを遷移しないためflash.nowを使います。

app/controllers/todos_controller.rb
class TodoControllers < ApplicationController
...
  def create
    @todo = Todo.new(todo_params)

    respond_to do |format|
      if @todo.save
	# 追加する
        format.turbo_stream { flash.now[:success] = "todoを作成されました!" }
      else
        format.turbo_stream do
	 # 追加する
          flash.now[:warning] = "タイトルを入力してください!"
          render turbo_stream: turbo_stream.replace("flash_messages", partial: "layouts/flash_messages")
        end   
...
      end
    end
  end
...
end

ビューにフラッシュメッセージのパーシャルを追加する

_flash_messages.html.erbのパーシャルがなかったら作成します。

app/views/todos/create.turbo_stream.erb
<%= turbo_stream.prepend "flash_messages", partial: "layouts/flash_messages" %>

そうすると表示されるようになりました。

todoを作成された時にフラッシュメッセージが表示されます:
Image from Gyazo

タイトルを空でtodoを作成するとバリデーションに引っかかります:
Image from Gyazo

turbo_streamパーシャル

コントローラーでrender: turbo_streamで書いても良かったですが、コードチェックの段階でFat Controllerと言われ通らなかったです。

なので、パーシャルを作ることにしました。
.html.erb.js.erbと同じように、アクションごとに作成していきます。

createビュー

_formパーシャルにid: "#{dom_id(todo)}_form"をつけしました。
todoを作成されましたら、フォームの中身をTodo.newにreplace・書き換えます。
フォームの下に、作成されたtodoを追加・appendします。

app/views/todos/create.turbo_stream.erb
# フラッシュメッセージ
<%= turbo_stream.prepend "flash_messages", partial: "layouts/flash_messages" %>
# todo
<%= turbo_stream.prepend "todos" do %>
  <%= render "todo", todo: @todo %>
<% end %>
# todoフォーム
<%= turbo_stream.replace "#{dom_id(Todo.new)}_form" do %>
  <%= render "form", todo: Todo.new %>
<% end %>

Image from Gyazo

updateアクション

elseブロック内のコードはcreateアクションのelse文と同じなので省略します。

app/controllers/todos_controller.rb
class TodoControllers < ApplicationController
...
  def update
    respond_to do |format|
      if @todo.update(todp_params)
	# 追加する
        format.turbo_stream{  flash.now[:success] = "todoを更新されました!" }
        format.html { redirect_to todo_url(@todo), notice: "Todo was successfully created." }
      else
	...# 省略
      end
    end
  end
...
end

updateビュー

フォームの内容を更新したら、todoの内容をreplace・書き換えます。

app/views/todos/update.turbo_stream.erb
<%= turbo_stream.replace dom_id(@todo) do %>
  <%= render "todo", todo: @todo %>
<% end %>

タイトルがないとtodoを作成されないことを確認します:
Image from Gyazo

更新しましたらメッセージが表示されます:
Image from Gyazo

destroyコントローラー

app/controllers/todos_controller.rb
class TodoControllers < ApplicationController
...
  def destroy
    @todo.destroy

    respond_to do |format|
      # 追加する
      format.turbo_stream { flash.now[:success] = "todoを削除しました!" }
      format.html { redirect_to todos_url, notice: "Todo was successfully destroyed." }
    end
  end
...
end

destroyビュー

app/views/destroy.turbo_stream.erb
<%= turbo_stream.prepend "flash_messages", partial: "layouts/flash_messages" %>
<%= turbo_stream.replace dom_id(@todo) do %>
  <%= render "todo", todo: @todo %>
<% end %>

todoを削除されました:

Image from Gyazo

ヘルパーメソッドを使う

重複なコードはヘルパーメソッドにまとめます。

<%= turbo_stream.prepend "flash_messages", partial: "layouts/flash_messages" %>
render turbo_stream: [
  turbo_stream.replace("flash_messages", partial: "shared/flash_messages")
]

ヘルパーメソッドを作成する

app/helpers/application_helper.rb
module ApplicationHelper
  def render_turbo_stream_flash_messages
    turbo_stream.prepend "flash", partial: "layouts/flash"
  end
  
  def render_flash_messages
      render turbo_stream: [
        turbo_stream.replace("flash_messages", partial: "shared/flash_messages")
      ]
   end
end

ヘルパーメソッドを読み込む

ビューに読み込みます。

<%# app/views/todos/create.turbo_stream.erb %>
<%# app/views/todos/update.turbo_stream.erb %>
<%# app/views/todos/destroy.turbo_stream.erb %>

<%= render_turbo_stream_flash_messages %>

コントローラーに読み込みます。

# createアクション
# updateアクション

  def update
    respond_to do |format|
      if @comment.update(comment_params)
      ...
      else
        format.turbo_stream do
	  ...
          render_flash_messages
        end
      end
    end
  end

終わりに

フラッシュメッセージが表示されましたのでここまでにします。
参考した記事:
https://www.hotrails.dev/turbo-rails/flash-messages-hotwire

Discussion