Topic: form_for and collection_select

After reading Rails Recipes, I decided that form_for was too cool to pass up. Gee, I can save at least 7 keystrokes per form smile

No, really, it does help improve consistency by allowing for custom builders. Here's my dilemma. I have a lot of belongs_to associations (who doesn't?) and use drop-downs to allow users to select their choices. collection_select allows for convenient population of the dropdowns, and the following code in the builder works great.

src = <<-END_SRC
def collection_select(field, collection, value_method, text_method, options={}, html_options={})
  @template.content_tag("tr",
    @template.content_tag("td", field.to_s.humanize + ":") +
      @template.content_tag("td", super))
end
END_SRC
class_eval src, __FILE__, __LINE__

However, what comes back to my controller is a string and not an id. Traditionally, the way to solve this problem was to do:

collection_select(object, associated_id, @collection, value_method, text_method)

What I'd like is:

src = <<-END_SRC
def associated_collection_select(field, collection, value_method, text_method, options={}, html_options={})
  @template.content_tag("tr",
    @template.content_tag("td", field.to_s.humanize + ":") +
      @template.content_tag("td",
        @template.collection_select(field, eval("#{field}_id"), collection, value_method,
          text_method, options={}, html_options={})
        ))
end
END_SRC
class_eval src, __FILE__, __LINE__

Is this a reasonable approach to getting an id back from the form? Comments?

Re: form_for and collection_select

cwd wrote:

However, what comes back to my controller is a string and not an id.

Are you referring to when the form is submitted? It should be the id of the selected item if :id is set as the "value method". For example, if user belongs to a group:

<% form_for :user do |f| %>
  <%= f.collection_select :group_id, Group.find(:all), :id, :name %>
<% end %>

When the form is submitted params[:user][:group_id] should be the id of the selected group. Sorry if I'm misunderstanding the problem.

Railscasts - Free Ruby on Rails Screencasts

Re: form_for and collection_select

I think I asked the question wrong. form_for does not [appear to] provide access to the collection_select helper. In any case, I want to modify the behavior slightly. The normal helper takes the parameters:

object, method, collection, value_method, text_method, options, html_options

In this case 'object' is the object that belongs_to something else. Maybe a concrete example:

class Bug
  belongs_to :project
end

so, I would use:

collection_select(@bug, :project_id, @projects, :id, :name)

The question I'm trying to solve is how to recast this in the form_for builder.

Re: form_for and collection_select

From my understanding, form_for supports all form helper methods (including collection_select). The only thing it changes is it automatically sets the first "object" parameter for each of the helpers so you don't have to repeat it. You can modify this behavior in a custom form builder, but I wouldn't recommend it as that seems to be more of an exception than a rule. Instead I would recommend changing the form object in the view directly and not in the form builder.

To do this, you can use fields_for inside of form_for. fields_for works just like form_for but doesn't include the form tag. The only downside to this is you have to repeat the form builder parameter:

<% form_for :user do |f| %> # add form builder parameter, etc.
  #...
  <% fields_for :bug do |b| %> # add form builder parameter, etc.
    <%= b.collection_select :project_id, Project.find(:all), :id, :name %>
  <% end %>
<% end %>

An alternative method is to set the object directly, but that doesn't feel as right to me:

<% form_for :user do |f| %> # add form builder parameter, etc.
  #...
  <% f.object_name = :bug %>
  <% f.object = @bug %>
  <%= f.collection_select :project_id, Project.find(:all), :id, :name %>
<% end %>

Does that solve your problem?

Last edited by ryanb (2006-09-06 21:23:20)

Railscasts - Free Ruby on Rails Screencasts