LoginSignup
39
48

More than 5 years have passed since last update.

Railsのフォームで個別にエラーメッセージを表示する

Posted at

Railsでフォームを作っていて、バリデーションのエラーメッセージをまとめて表示するのではなく、各パーツの下に個別表示したい…というニーズがありました。実際、上にエラーを表示するよりも該当箇所に表示したほうがユーザビリティが向上しそうです。

エラーメッセージは、@form.errors.full_messagesなどに格納されているので頑張ればなんとかできそうです。というわけで、2つほどやり方を試してみました。

field_error_procをカスタマイズ

わりとよく見る解決方法です。

config/initializers/error_customize.rb あたりにActionView::Baseのfield_error_procにProcをブチ込みます。

ActionView::Base.field_error_proc = Proc.new do |html_tag, instance|
  if instance.kind_of?(ActionView::Helpers::Tags::Label)
    html_tag.html_safe
  else
    method_name = instance.instance_variable_get(:@method_name)
    errors = instance.object.errors[method_name]
    html = <<~EOM
    <div class="has-error">
      #{html_tag}
      <span class="help-block">
        #{I18n.t("activerecord.attributes.#{instance.object.class.name.underscore}.#{method_name}")}
        #{errors.first}
      </span>
    </div>
    EOM
    html.html_safe
  end
end

これはこれでエラー文言を割りと自在に操れるんですが、collection_radio_buttons collection_selectなどの複数のフォームパーツを表示するようなヘルパーを使うと、パーツ分エラー文言が表示されてしまいます。

フォームビルダー

RailsのフォームオブジェクトはFormBuilderというクラスのインスタンスです。そしてサブクラスとして拡張することができます。これを使ってエラー文言をコントロールします。

app/form_builders/custom_form_builder.rb あたりにファイルを作ります。

元から定義されているヘルパーメソッドをオーバーライドしてエラーオブジェクトがあれば、それを表示し、それ以外の処理はsuperで任せるようにしています。下記コードは全てのフォームヘルパーを再定義しているわけじゃないので必要に応じて拡張してください。

class CustomFormBuilder < ActionView::Helpers::FormBuilder
  def text_field(method, options = {})
    super + error(method)
  end

  def number_field(method, options = {})
    super + error(method)
  end

  def telephone_field(method, options = {})
    super + error(method)
  end

  def collection_select(method, collection, value_method, text_method, options = {}, html_options = {})
    super + error(method)
  end

  def collection_radio_buttons(method, collection, value_method, text_method, options = {}, html_options = {}, &block)
    super + error(method)
  end

  private

  def error(method)
    error_html(error_message(method))
  end

  def error_message(method)
    (@object.errors[method].size > 0) ? I18n.t("activerecord.attributes.#{@object.model_name.singular}.#{method}") + @object.errors[method].first : ""
  end

  def error_html(msg)
    return "" unless msg.present?

    @template.content_tag(:div, class: "has-error") do
      @template.concat (@template.content_tag(:span, class: "help-block") do
        msg
      end)
    end
  end

end

そして使いたいフォームのViewで拡張したフォームビルダーを指定します。

= form_for @form, :builder => CustomFormBuilder do |f|

これで完了です。簡単ですね。

39
48
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
39
48