Read more

A saner alternative to SimpleForm's :grouped_select input type

Arne Hartherz
May 23, 2014Software engineer at makandra GmbH

SimpleForm Show archive.org snapshot is a great approach to simplifying your forms, and it comes with lots of well-defined input types. However, the :grouped_select type seems to be overly complicated for most use cases.

Example

Illustration online protection

Rails Long Term Support

Rails LTS provides security patches for old versions of Ruby on Rails (2.3, 3.2, 4.2 and 5.2)

  • Prevents you from data breaches and liability risks
  • Upgrade at your own pace
  • Works with modern Rubies
Read more Show archive.org snapshot

Consider this example, from the documentation:

form.input :country_id, collection: @continents,
  as: :grouped_select, group_method: :countries

While that looks easy enough at a first glance, look closer. The example passes @continents for a country_id.\
SimpleForm actually expects :collection to be a somewhat grouped collection (array of arrays, hash, ...) that it can read from.

In "the real world" you usually already have your list of items to select from, like @countries. To make that work with SimpleForm, you'd have to do crazy things like the following.

form.input :country_id, collection: @countries.group_by(&:continent_name),
  as: :grouped_select, group_method: :last, group_label_method: :first

Solution

Often times, you would want to say something like

form.input :country_id, collection: @countries,
  as: :grouped_select, group_by: :continent_name # new option "group_by"

or

form.input :country_id, collection: @countries,
  as: :grouped_select, group_by: proc { |country| country.continent.name }

Here is an extension to :grouped_select which will allow you to do exactly that, yet keeping SimpleForm's API in case you need it.

class GroupedCollectionSelectInput < SimpleForm::Inputs::GroupedCollectionSelectInput

  def input
    group_by = options.delete(:group_by)
    if group_by
      grouped_collection = options[:collection].group_by(&group_by)

      options[:collection] = grouped_collection
      options[:group_method] = :last
      options[:group_label_method] = :first
    end

    super
  end

end

Put that file into a place like app/inputs/grouped_collection_select_input.rb (SimpleForm will pick it up automatically; if it does not, restart your Rails server) and use it in your form with that new :group_by option.

Note that this only works if you are grouping by something that should be the human-readable optgroup label which is almost always the case.

Posted by Arne Hartherz to makandra dev (2014-05-23 12:54)