How can I add an "error" class to the input on error using simple_form?
Asked Answered
C

7

14

I need to add a class to the input/textarea/etc when the form is rendered and that field has an error.

<input type="text" class="custom-error-class" />

Is there an easy way to append to SimpleForm's list of CSS classes, but only when the field's corresponding object is an error?

Carruth answered 4/2, 2014 at 17:39 Comment(0)
R
7

I was having the same problem. My solution for this:

I created a new class StringInput (which overrides the original one) and copied the implementation out of the rdoc. I patched that code to check if there are errors on the field itself, if so I add a class invalid.

Because I wanted to use the wrapper options I added a error_class attribute into my initializer.

The full code:

app/inputs/string_input.rb

class StringInput < SimpleForm::Inputs::StringInput
  def input(wrapper_options = nil)
    unless string?
      input_html_classes.unshift("string")
      input_html_options[:type] ||= input_type if html5?
    end

    input_html_classes << wrapper_options[:error_class] if has_errors?
    merged_input_options = merge_wrapper_options(input_html_options, wrapper_options)

    @builder.text_field(attribute_name, merged_input_options)
  end
end

config/initializers/simple_form.rb

SimpleForm.setup do |config|
  config.error_notification_class = 'alert alert-danger'
  config.button_class = 'waves-effect waves-light btn'

  config.wrappers tag: :div, class: :input, error_class: :"error-field" do |b|
    # Form extensions
    b.use :html5
    b.optional :pattern
    b.use :maxlength
    b.use :placeholder
    b.use :readonly

    # Form components
    b.use :label
    b.use :input, class: 'validate', error_class: 'invalid'
    b.use :hint,  wrap_with: { tag: :span, class: :hint }
    b.use :error, wrap_with: { tag: :span, class: :error }
  end
end

This adds a defined error class onto all of your string inputs.

Rochester answered 13/9, 2015 at 8:44 Comment(1)
thanks!! you save me a lot of work, just exactly what i wanted to do =)Child
K
3

simple_form adds field_with_errors class to the wrapper element. You can use this to make your input look different:

.field_with_errors input { ... }
Kilburn answered 3/2, 2016 at 4:50 Comment(2)
Not sure why they downvoted your answer. But this is the one and only true way to update input error styles in a clean way out of the box. What baffles me is that people would write an entire new input class specifically for this case.Abbieabbot
This should be the correct answer! It is not very well explained in SimpleForm docs, but it works and it is... simple.Atween
K
3

It is easier now with simple_form 3.5.0.

Redefine existing input (i.e. StringInput) by creating a new class with the same name (docs). Then override input_html_classes method:

# app/inputs/string_input.rb
class StringInput < SimpleForm::Inputs::StringInput
  def input_html_classes
    has_errors? ? super.push('custom-error-class') : super
  end
end
Kielty answered 31/10, 2017 at 18:52 Comment(0)
S
1

You can do so using error_html option:

<%= f.input :attr, error_html: { class: 'custom-error-class' } %>
Syllabism answered 4/2, 2014 at 17:51 Comment(1)
This is close, but it adds the error class on the sibling error span, not the input itself.Carruth
K
1

Thanks @vince-v,

Using your information I came up with this work in progress for applying the error class to all types of inputs, including labels if they're configured with an error_class.

# lib/inputs/base.rb
module SimpleForm
  module Inputs
    class Base
      def merge_wrapper_options(options, wrapper_options)
        working_wrapper_options = wrapper_options.dup

        if working_wrapper_options
          if working_wrapper_options[:error_class] && has_errors?
            working_wrapper_options[:class] =
              [working_wrapper_options[:class]] + \
              [working_wrapper_options[:error_class]]
          end
          working_wrapper_options.delete(:error_class)

          working_wrapper_options.merge(options) do |key, oldval, newval|
            case key.to_s
            when "class"
              Array(oldval) + Array(newval)
            when "data", "aria"
              oldval.merge(newval)
            else
              newval
            end
          end
        else
          options.dup
        end
      end
    end
  end
end

# config/initializers/simple_form.rb
require 'inputs/base.rb
Kristoferkristoffer answered 21/8, 2016 at 16:45 Comment(0)
F
1

My solution was to create an initializer with the following:

inputs = %w[
  CollectionSelectInput
  DateTimeInput
  FileInput
  GroupedCollectionSelectInput
  NumericInput
  PasswordInput
  RangeInput
  StringInput
  TextInput
]

inputs.each do |input_type|
  superclass = "SimpleForm::Inputs::#{input_type}".constantize

  new_class = Class.new(superclass) do
    def input_html_classes
      if has_errors?
        super.push('form-control-danger')
      else
        super
      end
    end
  end

  Object.const_set(input_type, new_class)
end
Family answered 4/11, 2016 at 12:53 Comment(0)
S
0

Adding a new answer, since things seem to be yet simpler these days!

In the current version of simple_form (5.2.0), I have accomplished this using error_class on either b.use :label or b.use :label_input (depending on whether or not you want labels):

b.use :label_input, error_class: 'custom-error-class'
Spinster answered 11/5, 2023 at 0:12 Comment(0)

© 2022 - 2025 — McMap. All rights reserved.