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
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.