Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

The state of enums in simple_form #1668

Open
emilebosch opened this issue Aug 21, 2019 · 3 comments
Open

The state of enums in simple_form #1668

emilebosch opened this issue Aug 21, 2019 · 3 comments
Assignees

Comments

@emilebosch
Copy link

Hi all,

Out of curiosity, over the years, I've aways had to type extra code to let the enums work in simple form. Why is this still the case? Basically any other fields just works out of the box. Is there here a reason I am totally missing?

When I think simple form I think simplicity, no need to worry, and no typing too much. It just works for all other types except enums. Any reason why?

@emilebosch emilebosch changed the title Again enums The state of enums in simple_form Aug 21, 2019
@jonmchan
Copy link

jonmchan commented Aug 4, 2021

Here's what I'm using for enum in case it helps someone else - it seems to make everything work. If code is not interested in being changed, we can at least add this to the readme to make utilizing enums easy?

model/user.rb:

class User < ApplicationRecord
  ..
  enum user_type: { viewer: 0, editor: 1, admin: 2 }
end

view/users/_form.rb:

<%= f.input :user_type, collection: User.user_types.keys, selected: @user.user_type %>

@nashby nashby self-assigned this Mar 28, 2022
@tvongaza
Copy link

tvongaza commented Oct 11, 2023

Rails 7.1 now allows you to validate enum columns, which allows a bit more fine grained control of when to show blank options - https://api.rubyonrails.org/classes/ActiveRecord/Enum.html.

We've wrapped the CollectionSelectInput to ease the creation of enum drop downs to something like <%= f.input :user_type %>, setting the allow_blank & prompt settings as best we can, not sure if it is useful to others:

class EnumInput < SimpleForm::Inputs::CollectionSelectInput
  def initialize(builder, attribute_name, column, input_type, options = {})
    raise ArgumentError, "EnumInput requires an enum column." unless column.is_a? ActiveRecord::Enum::EnumType

    # Enum's are only required if we do not allow nil values
    inclusion_validator = builder.object.class.validators_on(attribute_name).find { |v| v.kind == :inclusion }
    options[:required] = inclusion_validator && !inclusion_validator&.options&.dig(:allow_nil)

    # If a prompt & include_blank are both present, we'll show 2 options before our enum values
    # priority is given to the prompt, so we'll remove the include_blank option
    #
    # If our enum is required, we remove the include_blank option (can't be nil)
    # This lets SimpleForm include it for new fields, and exclude for preset fields
    #
    # Otherwise we'll show a blank option before our enum values
    if options[:prompt].present? || options[:required]
      options.delete(:include_blank)
    else
      options[:include_blank] = true
    end

    super
  end
  def collection
    @collection ||= begin
      raise ArgumentError, "Collections are inferred when using the enum input, custom collections are not allowed." if options.key?(:collection)
      object.defined_enums[attribute_name.to_s].keys.map do |key|
        [object.class.human_enum_name(attribute_name, key), key] # Our i18n translations aren't fully standard so this may not apply to everyone. WIP.
      end
    end
  end
end

And rails initializer to register the type:

module RegisterEnumAsSimpleFormDefaultType
  def default_input_type(attribute_name, column, options)
    # If we are explicit about the type, use that
    return options[:as].to_sym if options[:as]

    if column.is_a? ActiveRecord::Enum::EnumType
      # If we are using an enum, use our custom EnumInput
      :enum
    else
      # Otherwise, use the default simple form type lookup
      super
    end
  end
end

# Ensure we prepend this module so it is called before the default lookup
SimpleForm::FormBuilder.prepend(RegisterEnumAsSimpleFormDefaultType)

@rhulse
Copy link

rhulse commented Dec 11, 2023

Thanks @tvongaza!

If anyone else wants to use this, you may need to change the class definition to this:

class EnumInput < SimpleForm::Inputs::CollectionSelectInput

I replaced the collection function with this:

def collection
    @collection ||= begin
      raise ArgumentError,
        "Collections are inferred when using the enum input, custom collections are not allowed." if options.key?(:collection)

      object.defined_enums[attribute_name.to_s].keys.map do |key|
        [key.to_s.capitalize, key]
      end
    end
end

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Development

No branches or pull requests

6 participants