趣旨
sessionは怖いので、redirect_toの引数としてflashを用いて別コントローラーに引き継ぐのがいいのでは。
どういうことか。
ときたま、pages_controllerなどで表示した静的ページの中で会員登録を行いたい場合がある。
今現在、/teaserというパスにおり、そこではpages_controller#teaserが呼び出されているとする。
そこで会員登録を行い、失敗した場合は再度同じパスに戻りエラーメッセージを表示させたいというのが今回の目的。
そのような場合でも、createはusers_controllerで行っていることが一般的であり、そうするとバリデーションに失敗してsave出来なかった場合の処理は以下のように書きたくなる。が、これでは当然上手く動かない。
# 静的ページを管理するコントローラー
class PagesController < ApplicationController
def teaser
@user = User.new()
render layout: 'teaser'
end
def teaser_thanks
render layout: 'teaser'
end
end
# @user.saveはUsersControllerで行いたい
class UsersController < ApplicationController
def create
@user = User.new(user_params)
if @user.save
redirect_to teaser_thanks_path # 成功すればティザーサイトのサンクスページヘ
else
# render 'new'ではなく、teaser_pathへ遷移
redirect_to teaser_path
end
end
private
def user_params
params.require(:user).permit(:email)
end
end
この時、別コントローラーのアクションを呼び出しているため、インスタンス変数に放り込まれているエラーメッセージ @user.errors
が受け渡されない
なので若干の工夫をする必要がある。
案1 sessionに渡す
この方法でも問題ない気はするが、以下の2つの理由から見送り。
- sessionはむやみに使いたくない
- 同じ画面に2つ同じフォームを置きたい場合(上部と下部にemail入力欄を設けたい、など)、先に出てくるフォームでsession[:error]が消されてしまい、それ以降のフォームではエラーメッセージが表示されない
- 工夫でなんとかできるが、viewがややこしくなるので好きではない
class UsersController < ApplicationController
def create
@user = User.new(user_params)
if @user.save
redirect_to teaser_thanks_path # 成功すればティザーサイトのサンクスページヘ
else
session[:error] = @user.errors.full_messages
redirect_to teaser_path
end
end
# 中略
end
# view
<%= form_for @user do |f| %>
<%= f.text_field :email, { placeholder: 'メールアドレス' } %>
<%= f.submit '事前登録' %>
<% if session[:error].present? %>
<ul class="errors">
<% session[:error].each do |e| %>
<li><%= e %></li>
<% end %>
</ul>
<% session[:error] = nil # 念のため使い終わったら消す %>
<% end %>
<% end %>
案2 素直にflashに渡す
30分ほど格闘し、flashに問題なく渡せることに気づいた。最初からこうすればよかった。
class UsersController < ApplicationController
def create
@user = User.new(user_params)
if @user.save
redirect_to teaser_thanks_path # 成功すればティザーサイトのサンクスページヘ
else
redirect_to teaser_path, flash: { error: @pre_regist.errors.full_messages }
end
end
# 中略
end
# view
<%= form_for @user do |f| %>
<%= f.text_field :email, { placeholder: 'メールアドレス' } %>
<%= f.submit '事前登録' %>
<% if flash[:error].present? %>
<ul class="errors">
<% flash[:error].each do |e| %>
<li><%= e %></li>
<% end %>
</ul>
<% end %>
<% end %>
終わりに
rails 楽しい。
が、こういうところで細かく悩まずに済むようにいち早く精進したい。