Rails options_from_collection_for_select with custom attributes for each option

You probably already know that you can use options_from_collection_for_select helper method generate <option></option> from a collection. If you are unaware of it, you can check this api documentation.

It helped me greatly until I needed some custom attributes to each <option></option> which will be used by jQuery for doing some tasks in UI level. Custom attributes will help me by reducing server calls for getting those information. However, I did not find a way set custom attributes with the above helper method. So, I’ve made a basic version of my own (if you prefer, you can override the original method). Put the following method in your helper file (may be in application_helper.rb).

def options_from_collection_for_select_with_data(collection, value_method, text_method, selected = nil, data = {})
    options = collection.map do |element|
      [element.send(text_method), element.send(value_method), data.map do |k, v|
        {"data-#{k}" => element.send(v)}
      end
      ].flatten
    end
    selected, disabled = extract_selected_and_disabled(selected)
    select_deselect = {}
    select_deselect[:selected] = extract_values_from_collection(collection, value_method, selected)
    select_deselect[:disabled] = extract_values_from_collection(collection, value_method, disabled)

    options_for_select(options, select_deselect)
  end

Now, when you need to use this method, call it as below:

options_from_collection_for_select_with_data(@blogs, :id, :title, nil,
  {:'post-type' => :type, :'post-slug' => :slug})

Note that, this will call the type and slug method of your object. It will not directly use the :type and :slug as your attributes value. Consider @blogs has only one blog item as follows:

{:id => 101, :title => 'Test Blog 1', :type => 'post', :slug => 'test-blog-1'}

so, the option will be like:

<option value="101" data-type="post" data-slug="test-blog-1">Test Blog 1</option>

As you see, data- will be prepended to your attribute names. So, you do not need to add them yourself.

This is basic version of what I’ve implemented and surely can be improved. Suggest me please.

  • Nhm tanveer

    An good catch, btw u don’t need to declare options = [] beforehand

  • The HungryCoder

    right, you’re! i put it earlier when i did not assigned the map result to it but push new items from inside loop. later forgot to remove it. thanks

  • Thanks a ton for this post. It was really helpful 🙂

  • timmy

    Over a year old and still has it’s uses. Should definitely be added into the rails repo.

  • It is possible using `options_for_select`. See this http://stackoverflow.com/a/6374301/121858

  • Manish Mukherjee

    thanks rohit, that’s a much simpler way of doing it.