Open emilebosch opened 5 years ago
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 %>
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)
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
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?