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 UI/UX Design

UI/UX Design by makandra brand

We make sure that your target audience has the best possible experience with your digital product. You get:

  • Design tailored to your audience
  • Proven processes customized to your needs
  • An expert team of experienced designers
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)