Plugataryov Yura
Plugataryov Yura

Reputation: 168

simple_form - can't generate combined select input

I have complex form with a lot of controls, and I'm currently using simple_form gem due to its flexibility. But when I wanted to do something more complex I faced to several problems that currently looking obscure for me. I would like to introduce combined collection input, that will render both optgroups and single non-groupped selects. Generated html that I want to achieve should look something like this:

<select name="select" multiple="multiple">
  <option value="1">Milk</option>
  <optgroup label="Soda">
    <option value="2">Cola</option>
    <option value="3">Fanta</option>
  </optgroup>
</select>

I've tried to create custom input class, but stucked on implementation details of input method, I simply couldn't find out how to generate proper output.

UPDATE

Currently quick and dirty implementation for custom input looks something like this, but I don't think that it is a good idea to drop all goodnesses with options that simple_form gives me.

class CombinedMultiselectInput < SimpleForm::Inputs::CollectionSelectInput
  include ActionView::Helpers::FormTagHelper
  include ActionView::Helpers::FormOptionsHelper

  def input
    out = ActiveSupport::SafeBuffer.new
    option_tags = ungrouped_options.safe_concat(grouped_options)
    out << select_tag(options[:name], option_tags, class: ['select', 'form-control'])
    out
  end

 private

 def ungrouped_options
   # this can be retrieved from general collection like collection[:ungrouped]
   collection = [["Foo", 2], ["Bar", 3]]
   options_for_select(collection)
 end

 def grouped_options
   # and this using collection[:grouped]
   collection = [["Group", [["Foobar", 4]]]]
   grouped_options_for_select(collection)
 end
end

Upvotes: 3

Views: 462

Answers (1)

engineersmnky
engineersmnky

Reputation: 29318

Using your current design you can combine the options_for_select and option_groups_from_collection_for_select methods like so.

def ungrouped_options
 [["Foo", 2], ["Bar", 3]]
end

def grouped_options
  [["Group", [["Foobar", 4]]]]
end

def your_hash
  {"ungrouped" => ungrouped_options, "grouped" => grouped_options}
end

Then in your view something like this should work:

<%= content_tag(:select,nil,{multiple: true,name: "select"}) do
  <%= your_hash.each do |k,v| %>
     <% if k == "ungrouped" %>
       <%= options_for_select(v) %> 
     <% else %>
       #this works because:
       # last will be the collection of children for a member
       # first will be the group name
       # last on the child will be the value method 
       # first on the child will be the text displayed
       <%= option_groups_from_collection_for_select(v, :last, :first, :last, :first)  %>
     <% end %>
  <% end %>
<% end %> 

This will create the following:

<select name=\"select\" multiple=\"true\">
    <option value=\"2\">Foo</option>
    <option value=\"3\">Bar</option>
    <optgroup label=\"Group\">
        <option value=\"4\">Foobar</option>
    </optgroup>
</select>

Obviously this was simplified to show how this could be done but hopefully this points you in the right direction.

You should also be able to wrap this for simple_form although I have not tested it.

<%= f.input :some_attribute do %>
  <%= f.select :some_attribute do %>
    <%= your_hash.each do |k,v| %>
      <% if k == "ungrouped" %>
        <%= options_for_select(v) %> 
      <% else %>
        <%= option_groups_from_collection_for_select(v, :last, :first, :last, :first)  %>
      <% end %>
    <% end %>
  <% end %>
<% end %>

Upvotes: 2

Related Questions