はじめに
フォームから送られるパラメータがモデルのバリデーションに引っかかった場合、エラー内容をビューに表示する必要があります。
Railsではそのための仕組みがあらかじめ用意されているので、比較的簡単に実装することが出来ます。
ただし、一部そのままでは使いにくい箇所があるので、そのあたりも含めてエラー時の処理をまとめたいと思います。
実装
なにはともあれエラー処理の基本を実装していきます。記事の新規作成時の処理をサンプルコードとしています。
コントローラー
ビューから送られてきたタイトルと本文を使ってレコードを新規作成します。
新規作成するときにモデルのバリデーションに引っかかった場合、レコードは作成されずエラー内容とともにビューに戻されます。
class ArticlesController < ApplicationController
def create
@article = Article.new(articles_params)
if @article.save
redirect_to articles_path
else
render :new
end
end
private
def articles_params
params.require(:article).permit(:title, :content)
end
end
余談ですが、エラー時にredirect_to
ではなくrender
を使っている理由は、render
はページ遷移時にコントローラーを介さないため、エラー内容を含んだパラメータをそのままビューに渡せるためです。
redirect_to
を使ってしまうと、new
メソッドが実行され、パラメータが上書きされることによりエラー内容がビューで参照できなくなってしまいます。
モデル
タイトルと本文に必須のバリデーションを実装します。
class Article < ApplicationRecord
validates :title, presence: true
validates :content, presence: true
end
ビュー
Rails5以降ではフォームの生成にform_with
ヘルパーメソッドの使用が推奨されています。form_for
やform_tag
も使えますが、将来廃止される可能性もあるので使わないほうが無難でしょう。
form_with
はデフォルトで非同期で処理を行うので、local: true
オプションを忘れないようにします。これを忘れると、コントローラーからエラー内容を受け取ることが出来ません。
_form_html.erb
<%= form_with model: @article, local: true do |form| %>
<%= render 'shared/errors', { target: @article } %>
...
<% end %>
shared/_errors.html.erb
<% if target.errors.any? %>
<div class="text-danger" role="alert">
<h3>エラー項目があります</h3>
<ul>
<% target.errors.full_messages.each do |message| %>
<li><%= message %></li>
<% end %>
</ul>
</div>
<% end %>
以上で基本的なエラー時の処理が実装できました。
日本語化
エラー内容はデフォルトでは英語でしか表示されないので日本語化します。
Gem追加
Gemfile
に以下を追記してbundle install
を行います。
gem 'rails-i18n'
application.rb変更
config/application.rb
に以下を追記します。
...
module AppName
class Application < Rails::Application
# Initialize configuration defaults for originally generated Rails version.
config.load_defaults 6.0
# Settings in config/environments/* take precedence over those specified here.
# Application configuration can go into files in config/initializers
# -- all .rb files in that directory are automatically loaded after loading
# the framework and any gems in your application.
# 以下を追記
config.time_zone = 'Tokyo'
config.active_record.default_timezone = :local
config.i18n.default_locale = :ja
config.i18n.load_path += Dir[Rails.root.join('config', 'locales', '**', '*.{rb,yml}').to_s]
end
end
日本語化ファイル作成
config/locales/ja.yml
を作成し、以下を記述します。
ja:
activerecord:
attributes:
article:
title: タイトル
content: 本文
以上でエラー内容の日本語化が出来ました。
スタイルを整える
エラーが発生しビューに戻ってくると、エラー項目を囲む.field_with_errors
というスタイルが自動的に付与されます。
...
<div class="form-group row">
<%= form.label :title, 'タイトル(必須)', class: 'col-12 col-sm-2 col-form-label' %>
<div class="col-12 col-sm-10">
<%= form.text_field :title, class: 'form-control', placeholder: 'タイトル' %>
</div>
</div>
...
エラーが発生すると以下のようになります。
...
<div class="form-group row">
<div class="field_with_errors">
<%= form.label :title, 'タイトル(必須)', class: 'col-12 col-sm-2 col-form-label' %>
</div>
<div class="col-12 col-sm-10">
<div class="field_with_errors">
<%= form.text_field :title, class: 'form-control', placeholder: 'タイトル' %>
</div>
</div>
</div>
...
Bootstrapを使用していると、自動付与された.field_with_errors
によりスタイルが崩れてしまいます。
これを回避するにはスタイルを上書きする必要があります。
app/assets/stylesheets/application.scss
に以下を追記します。
...
.field_with_errors {
display: contents;
input, textarea {
@extend .is-invalid;
}
}
...
.field_with_errors
の表示を変更するとともに、内包するinput
、textarea
に.is-invalid
のスタイルを適用させています。
この書き方はBootstrapを使っている場合に限られますのでご了承ください。
まとめ
エラー時の処理はRailsチュートリアルにも書いてある基本的な機能ですが、忘れやすいところですし、それだけでは不十分だったのでまとめてみました。