Rails multiple select helper

ruby rails form tag helpers habtm

Mon Sep 14 07:39:19 +0000 2009

I’ve just written a little Rails helper to output a multiple select tag with the correct options selected by default. I’ve always struggled with this when working with a HABTM relationship in Rails apps, so thought it was about time to come up with my own solution. As far as I’m aware, collection_select is generally the preferred helper in this situation but I found that it clashed with my brain in the worst way.

Anyway… here it is (line breaks are marked »):

def multi_select(id, name, objects, selected_objects, value, content = {})
  options = ""
  objects.each do |o|
    selected = selected_objects.include?(o) ? " selected=\"selected\"" : ""
    option_value = escape_once(o.send(value))
    text = [option_value]
    unless content[:text].nil?
      text = []
      content[:text].each do |t|
        text << o.send(t)
      end
      text = text.join(" ")
    end
    bracket = []
    unless content[:bracket].nil?
      content[:bracket].each do |b|
        bracket << o.send(b)
      end
      bracket = bracket.join(" ")
    end
    option_content = bracket.empty? ? "#{text}" : "#{text} (#{bracket})"
    options << "<option value=\"#{option_value}\"#{selected}>#{option_content} »
                </option>\n"
  end
  "<select multiple=\"multiple\" id=\"#{id}\" name=\"#{name}\">\n#{options}</select>"
end

(Code Gist at GitHub)

The helper is used as such…

multi_select("tag_id", "tag_name", @users, @selected_users, :id,
             {:text => [:username], :bracket => [:first_name, :last_name]})

…where the value argument is a single symbol and the content argument is a hash. The content argument works as follows: the :text option calls each value as a method on the object to produce the option tag text (in this example, @user.username is called). If omitted, :text defaults to whatever was passed to the value argument (in this case :id, which calls @user.id). The :bracket option is just something I needed for a certain project and will most likely be omitted in general use. The above example produces something like this:

I hope this is of use to someone else. I’m always interested to hear of refinements or better alternatives too.

blog comments powered by Disqus