12 October 2009 · About 2 minutes read

Rails: Getting the id of form fields inside a fields_for block

Something that seems to crop up a lot when working with nested forms is the need to access the id of an input field generated inside a fields_for loop. The problem is that the FormBuilder object doesn’t expose its index attribute, so you can’t specify the full id of the field. For example:

```ruby# app/views/people/_form.html.erb

<% person_form,fields_for :attributes do attributes_form -%>

# This text field’s id will be in the format person_attributes_[index]_name

<%= f.text_field :name %>

# No way to know what [index] should be

<%= observe_field :person_attributes_[index]_name, … %>

<% end -%>```

I hit this problem a few days ago when I wanted to setup autocompleters on a dynamically generated field. I ended up posting a solution to Google Groups. For completeness, I’m reposting the code with all my typos removed.

Where does the index come from?

To find out how the FormBuilder generates its field ids, I delved into the Rails source code and found a couple of helper methods in the InstanceTagMethods module (source).

The methods of interest are sanitized_object_name and sanitized_method_name. They take the name of the FormBuilder’s model object (which is publicly accessible) and return the generated form element id (e.g. person[attributes][0][name] becomes person_attributes_0_name).

However, the methods that work this magic are private to the InstanceTagMethods module, so you can’t get at them from within your fields_for block.

Exposing the generated form field ids

I decided to duplicate these methods in my application_helper.rb, making them available to my fields_for block. Whilst not DRY, this is so far the only solution I’ve found for exposing the FormBuilder’s index:

```ruby# app/helpers/application_helper.rb

def sanitized_object_name(object_name)

object_name.gsub(/][ [^-a-zA-Z0-9:.]/,””).sub(/$/,””)

end

def sanitized_method_name(method_name)

method_name.sub(/?$/, “”)

end

def form_tag_id(object_name, method_name)

”#{sanitized_object_name(object_name.to_s)}_#{sanitized_method_name(method_name.to_s)}”

end```

```ruby# app/views/people/_person_attributes.rb

wrapped in person_form,fields_for :attributes do |attributes_form|

<%= f.text_field :name %>

<%= observe_field form_tag_id(f.object_name, :name), … %>```

This code works for indexed partials (i.e. those with indexes based on their model id), and those generated with timestamp indexes such as in Ryan Bates’ latest nested form code for Rails 2.3 -

http://github.com/ryanb/complex-form-examples.

Chris Blunt
Chris Blunt @cblunt
Chris is the founder of Plymouth Software. As well as code and business, he enjoys being a Dad, swimming, and the fine art of drinking tea.