[Rails7]turboでフラッシュメッセージを表示させる
はじめに
初めて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
を使います。
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
のパーシャルがなかったら作成します。
<%= turbo_stream.prepend "flash_messages", partial: "layouts/flash_messages" %>
そうすると表示されるようになりました。
todoを作成された時にフラッシュメッセージが表示されます:
タイトルを空でtodoを作成するとバリデーションに引っかかります:
turbo_stream
パーシャル
コントローラーでrender: turbo_stream
で書いても良かったですが、コードチェックの段階でFat Controllerと言われ通らなかったです。
なので、パーシャルを作ることにしました。
.html.erb
と.js.erb
と同じように、アクションごとに作成していきます。
createビュー
_form
パーシャルにid: "#{dom_id(todo)}_form"
をつけしました。
todoを作成されましたら、フォームの中身をTodo.new
にreplace・書き換えます。
フォームの下に、作成されたtodoを追加・appendします。
# フラッシュメッセージ
<%= 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 %>
updateアクション
elseブロック内のコードはcreateアクションのelse文と同じなので省略します。
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・書き換えます。
<%= turbo_stream.replace dom_id(@todo) do %>
<%= render "todo", todo: @todo %>
<% end %>
destroyコントローラー
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ビュー
<%= turbo_stream.prepend "flash_messages", partial: "layouts/flash_messages" %>
<%= turbo_stream.replace dom_id(@todo) do %>
<%= render "todo", todo: @todo %>
<% end %>
todoを削除されました:
ヘルパーメソッドを使う
重複なコードはヘルパーメソッドにまとめます。
<%= turbo_stream.prepend "flash_messages", partial: "layouts/flash_messages" %>
render turbo_stream: [
turbo_stream.replace("flash_messages", partial: "shared/flash_messages")
]
ヘルパーメソッドを作成する
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
終わりに
フラッシュメッセージが表示されましたのでここまでにします。
参考した記事:
Discussion