Topic: Select Menu with Groups

I am attempting to create a select menu with groups from some static content in my model. I am having trouble figuring out how to get the select form helper to implement the optgroup tag to group the options.

Basically I am trying to generate the following html code:

<select id="order_drink_type" name="order[drink_type]">
  <optgroup label="Soda">
    <option value="HazelnutSoda">Hazelnut</option>
    <option value="BirchBeerSoda">Birch Beer</option>
    <option value="CoconutSoda">Coconut</option>
  </optgroup>
  <optgroup label="Juice">
    <option value="BlueberryJuice">Blueberry</option>
    <option value="StrawberryJuice">Strawberry</option>
  </optgroup>
</select>

From the following model definition:

def drink_types
  ['Soda' => [
              {'name' => 'HazelnutSoda', 'display' => 'Hazelnut'},
              {'name' => 'BirchBeerSoda', 'display' => 'Birch Beer},
              {'name' => 'CoconutSoda', 'display' => 'Coconut'}
            ], 
    'Juice => [
              {'name' => 'BlueberryJuice', 'Blueberry'},
              {'name' => 'StrawberryJuice', 'Strawberry'}
            ]   
  ]
end

I would like to use a simple select form helper inside a form_for tag if that is possible. For example:

<% form_for @order do |f| %>
  <%= f.select :drink_type, @order.drink_types %>
<% end %>

This of course only lists the drink categories as options rather than listing the actual drink types as options grouped by the categories using the optgroup tag. Is there an easy way to implement this with rails or am I going to have to just hard code it in the HTML?

Thanks.

Re: Select Menu with Groups

I've been playing around with option_groups_from_collection_for_select but haven't been able to get it to reference pieces of the hash yet.

I refactored the model to include a identifiers for the group names and the data in each group:

 def drink_types
   ['category' => 'Soda', 'types' => [
               {'name' => 'HazelnutSoda', 'display' => 'Hazelnut'},
               {'name' => 'BirchBeerSoda', 'display' => 'Birch Beer},
               {'name' => 'CoconutSoda', 'display' => 'Coconut'}
             ], 
     'category' => 'Juice, 'types' => [
               {'name' => 'BlueberryJuice', 'Blueberry'},
               {'name' => 'StrawberryJuice', 'Strawberry'}
             ] 
   ]
end

And then attempted to implement the option_groups_from_collection_for_select helper as follows:

<%= f.select :type, option_groups_from_collection_for_select(@service.available_types, :types, :category, :name, :display) %>

But it doesn't recognize the hash keys as methods and throws a undefined method 'category' error.

I feel like this is the right approach on the front-end but I'm not setting up the model correctly. How could I structure a hash to work with the option_groups_from_collection_for_select helper?

Re: Select Menu with Groups

Ended up just rewriting the helper methods for select and option_groups_from_collection_for_select. Consider this solved.

Re: Select Menu with Groups

Please, can you share your solution please?
Thanks.

Re: Select Menu with Groups

In the model: app/models/drink.rb

  def available_types
    [   
      {'category' => 'Soda', 'types' => [
          {'display' => 'Hazelnut', 'name' => 'HazelnutSoda'},
          {'display' => 'Birch Beer', 'name' => 'BirchBeerSoda'}
      ]},
      {'category' => 'Juice', 'types' => [
          {'display' => 'Blueberry', 'name' => 'BlueberryJuice'},
          {'display' => 'Strawberry', 'name' => 'StraberryJuice'}
      ]} 
    ]   
  end

In the application helper: app/helpers/application_helper.rb

  def select_with_groups_from_hash(object, method, choices)                                            
    name = object.class.name.downcase
    html = "<select id=\"#{name}_#{method}\" name=\"#{name}[#{method}]\">"                             
    html += choices
    html += "</select>"
  end

  def option_groups_from_hash_for_select(hash, group_method, group_label_method, option_key_method, option_value_method, selected_key = nil)
    hash.inject("") do |options, group|                                                               
      group_string = eval("group['#{group_method}']")                                                 
      group_label_string = eval("group['#{group_label_method}']")
      options += "<optgroup label=\"#{group_label_string}\">"                                         
      options += options_from_hash_for_select(eval("group['#{group_method}']"), option_key_method, option_value_method, selected_key)
      options += '</optgroup>'                                                                         
    end
  end

  def options_from_hash_for_select(collection, value_method, text_method, selected = nil)             
    options = collection.map do |element|
      [eval("element['#{text_method}']"), eval("element['#{value_method}']")]                         
    end
    options_for_select(options, selected)
  end


And finally in the view: app/views/drinks/new.html.erb

<%= select_with_groups_from_hash(@drink, :type, option_groups_from_hash_for_select(@drink.available_types, :types, :category, :name, :display)) %>

Enjoy smile

Re: Select Menu with Groups

Many thanks, it works, congratulation!!

Thanks a lot.

Re: Select Menu with Groups

#continentes = [['Africa',['NA','Namibia'],...],...]
  def option_groups_for_select(collection,selected = nil)
    collection.inject('') do |options_for_select,group|
      group_label_string = group[0]
      options_for_select = "<optgroup label=\"#{html_escape(group_label_string)}\">"
      options_for_select += options_for_select(group[1..-1],selected)
      options_for_select += '</optgroup>'
    end
  end

Last edited by grosser (2008-09-17 07:28:38)

Re: Select Menu with Groups

this is modified so _ALL_ the groups show,

def option_groups_for_select(collection,selected = nil)
     options_for_select=String.new
     collection.inject('') do |options_for_select,group|
        group_label_string = group[0]
        options_for_select += "<optgroup label=\"#{html_escape(group_label_string)}\">"
        options_for_select += options_for_select(group[1..-1],selected)
        options_for_select += '</optgroup>'
     end
end

also remember that if the collection is in the database you can use the action view helper:
option_groups_from_collection_for_select