dontangg
dontangg

Reputation: 4809

Create a select tag with some options grouped and others not grouped

I'm using Rails 3. I want to create a select tag with some options grouped and others not grouped. Options would look something like this:

Income
Auto
  Fuel
  Maintenance
Home
  Maintenance
  Mortgage

In this example, Income is not a group, but Auto and Home are.

I see three helper methods grouped_options_for_select and grouped_collection_select, option_groups_from_collection_for_select but they all appear to require that you have a group for every option.

Is there a way to use a helper to do this or will I have to generate the HTML myself? I imagine I could use two different helpers to create the options and just append the results of both.

Upvotes: 2

Views: 1298

Answers (2)

rprez
rprez

Reputation: 525

Using Aarons answer as a starting point, I made a version that takes a Hash as the input.

  def grouped_and_ungrouped_options_for_select(grouped_options, selected_key = nil)
    body = ''
    grouped_options.each do |key, value|
      selected = selected_key == value
      if value.is_a?(Hash)
        body << content_tag(:optgroup, grouped_and_ungrouped_options_for_select(value, selected_key), :label => key)
      else
        body << content_tag(:option, key, value: value, selected: selected)
      end
    end
    body.html_safe
  end

Upvotes: 0

Aaron Breckenridge
Aaron Breckenridge

Reputation: 1819

There is no off-the-shelf helper (that I know of) that can do what you need. It's somewhat difficult to do because it's going to depend on your data model. Is it an Array, Hash, parent-child, or many-to-many relationship?

Assuming that it's parent-child, you could use recursion to build it:

def child_options_for_select(collection, children_method, group_label_method, child_value_method, child_label_method, options = {})
  body = ''
  collection.each do |item|
    children = item.send(children_method)
    if item.children.count != 0
      body << content_tag(:optgroup, child_options_for_select(children, children_method, group_label_method, child_value_method, child_label_method, options), :label => item.send(group_label_method))
    else
      body << content_tag(:option, item.send(child_label_method), :value => item.send(child_value_method))
    end
  end
  body.html_safe
end

Example usage in your view:

<%= select_tag 'foo', child_options_for_select(@categories.roots, :children, :to_s, :id, :to_s) %>

Note that this is fairly slow as it involves several round trips to the database.

Upvotes: 3

Related Questions