はじめに
Rails6を使っている教材を、Rails8で取り組んでいます。
バリデーションエラーが表示されずハマったので、原因と解決策を残しておきます。
やりたいこと
- Validationエラーの内容を表示したい
やったこと
- Modelにvalidatesの記述
validates :title, presence: true validates :content, presence: true
- viewに@article.errors.full_messagesをeachメソッドで出力できるように記載
<ul> <% @article.errors.full_messages.each do |message| %> <li><%= message %></li> <% end %> </ul>
エラー内容
- 空のまま送信してもエラーメッセージが表示されない
- ループ処理内に記載したliタグ自体が生成されていない
調査
-
コンソール上で確認したところ正常にValidationが効いている
myapp(dev)> article = Article.new => #<Article:0x00007f1d3978bf88 id: nil, title: nil, content: nil, created_at: nil, updated_at: nil> myapp(dev)> article.valid? => false myapp(dev)> puts article.errors.full_messages Title can't be blank Content can't be blank => nil
-
オブジェクトにエラーが入っているかを確認:正常に動作していそう
def create @article = Article.new(article_params) if @article.save redirect_to article_path(@article) else Rails.logger.debug "エラー内容: #{@article.errors.full_messages.inspect}" render :new end end
- ログ
エラー内容: ["Title can't be blank", "Content can't be blank"]
原因
Rails 7 では Turbo
がデフォルトで有効になった
-
form_with
を使うと、デフォルトでremote: true
(非同期リクエスト)として扱われる - その結果、フォーム送信後のレスポンスが
Turbo Stream
として処理される
非同期処理の場合、render :new
の結果が Turbo
によって正しく適用されない
- Rails 6 では
render :new
すると、HTMLがそのまま更新される - Rails 7 のデフォルト(
Turbo
有効)では、サーバーが200 OK
を返すと、画面は更新されるが@article.errors
が適切に反映されないことがある
422 ステータス(unprocessable_entity
)を返さないと、バリデーションエラー時のフォーム描画が正しく動かない
-
render :new
のデフォルトのステータスは200 OK
であるため、エラー時にも通常のページ遷移と同じ挙動をしてしまう -
Turbo
は200 OK
を受け取ると、「成功した」と判断してしまう -
unprocessable_entity
(422)を返せば、「フォームの再描画が必要なエラー状態」と正しく認識される
解決策
-
Turboをfalseにする
= f.button :submit, data: { turbo: false }
-
422ステータスを返すようにする
render :new, status: :unprocessable_entity
どちらを選ぶか
個人的にはせっかくなのでTurboを使える422のほうが良いかと思い、こちらを選びました。
そもそもTurboはUX向上の為にデフォルトになったはずなので、あえて意図的に停止させなくてもよいかなと…
あと、フォームごとにTurboだったりTurboじゃなかったりすると、分かりづらいコードになってしまうと考えました。
終わりに
Railsのアップデートについてもっと学習が必要だと感じました。
参考にあげたZenn本にも時間があるときに取り組んでみようと思います。