操作結果のメッセージを、ちゃんと出したい
Railsでアプリを作っていると、ユーザーにメッセージを出したい場面がよくあります。
- 登録が完了しました
- 更新しました
- 削除しました
- 入力内容を確認してください
- ログインしてください
こういう操作結果の表示には、Railsの flash が便利です。
ただ、最初に少し迷いやすいのが、flash と flash.now の違いです。
この記事では、redirect_to と render の違いから、flash と flash.now の使い分けを整理します。
Railsのflashは、次のリクエストまで値を渡すもの
Rails Guides では、flash は controller action 間で一時的なデータを渡す仕組みとして説明されています。
Action Controller Overview - Ruby on Rails Guides
よく使うのは、リダイレクト後にメッセージを表示するケースです。
class UsersController < ApplicationController
def create
@user = User.new(user_params)
if @user.save
flash[:notice] = "ユーザー登録が完了しました"
redirect_to @user
else
render :new, status: :unprocessable_entity
end
end
end
flash[:notice] に入れたメッセージは、redirect_to した次のリクエストで使えます。
登録完了後に詳細画面へ遷移し、そこで「ユーザー登録が完了しました」と表示するような使い方です。
レイアウトに表示処理を書いておく
flash の表示は、各ページに毎回書くより、レイアウトにまとめると楽です。
<% flash.each do |type, message| %>
<div class="flash flash-<%= type %>">
<%= message %>
</div>
<% end %>
app/views/layouts/application.html.erb などに置いておけば、どのページでも共通で表示できます。
CSSも notice と alert で分けておくと、成功と失敗が見分けやすくなります。
.flash-notice {
color: #0f5132;
background: #d1e7dd;
}
.flash-alert {
color: #842029;
background: #f8d7da;
}
flash.now は同じリクエストで表示したいときに使う
迷いやすいのが、保存に失敗して render :new する場合です。
render は新しいリクエストではありません。
同じリクエスト内でテンプレートを表示します。
この場合は flash.now を使います。
class UsersController < ApplicationController
def create
@user = User.new(user_params)
if @user.save
redirect_to @user, notice: "ユーザー登録が完了しました"
else
flash.now[:alert] = "入力内容を確認してください"
render :new, status: :unprocessable_entity
end
end
end
flash.now は、そのリクエスト内だけで使うメッセージです。
render で同じ画面を表示する場合に向いています。
redirect_to なら flash、render なら flash.now
まずは、この整理で十分です。
redirect_to する場合:
flash または redirect_to の notice / alert を使う
render する場合:
flash.now を使う
リダイレクトなら次のリクエストに渡す必要があります。
renderなら同じリクエストで表示したいだけです。
ここを混ぜると、メッセージが次の画面に残ったり、逆に表示されなかったりします。
よくあるミス
renderなのに flash を使う
flash[:alert] = "入力内容を確認してください"
render :new
この書き方でも、その場で表示されることがあります。
ただ、次のリクエストまでメッセージが残ってしまい、別ページで同じ alert が出ることがあります。
render のときは flash.now を使うほうが意図が明確です。
redirectなのに flash.now を使う
flash.now[:notice] = "更新しました"
redirect_to @user
これは、リダイレクト先ではメッセージが表示されません。
flash.now は同じリクエスト内だけなので、redirect で次のリクエストへ行くと消えます。
何でもflashに入れすぎる
flash は便利ですが、何でも入れるものではありません。
基本は一時的なメッセージに使うのがよいです。
- 成功通知
- エラー通知
- 注意メッセージ
- ログイン要求
長いデータや複雑な状態管理は、別の方法を考えたほうがよいです。
実務での使い分け例
自分なら、次のように使います。
def update
if @user.update(user_params)
redirect_to @user, notice: "プロフィールを更新しました"
else
flash.now[:alert] = "入力内容を確認してください"
render :edit, status: :unprocessable_entity
end
end
成功時は詳細画面へリダイレクトするので notice。
失敗時は編集画面をそのまま表示するので flash.now[:alert]。
この形にしておくと、読み手にも意図が伝わりやすいです。
Railsのenumを使いこなす のようなRails記事と同じく、Railsの便利機能は「いつ使うか」を整理するとかなり扱いやすくなります。
まとめ
flash と flash.now の違いは、リクエストをまたぐかどうかで考えると分かりやすいです。
-
flashは次のリクエストまで残る -
flash.nowは今のリクエストだけで使う -
redirect_toならflash -
renderならflash.now - 表示処理はレイアウトにまとめると楽
最初は少し紛らわしいですが、redirect_to と render の違いに合わせて考えれば、かなりすっきりします。
操作結果のメッセージは小さな部分ですが、ユーザーにとっては大事です。
Railsの flash をうまく使って、分かりやすい画面にしていきたいですね。