Using observe_field on an field inside a fields_for

I'm trying to observe a field that get generated inside a fields_for loop (I'm trying to create the form dynamically depending on a selection value). Is there a way to access the index in the field_for loop?

Thanks, Jay

Hi Jay,

OK, here's an example of what I'm trying to do.

_form.html.erb

<%= form.error_messages %> <p>   <%= form.label :title %><br />   <%= form.text_field :title %> </p> <p>   <%= form.label :due_date %><br />   <%= form.datetime_select :due_date %> </p> <div id="tasks">   <h3>Tasks</h3>   <% form.fields_for :tasks do |task_form| -%>     <%= render :partial => 'task', :locals => { :form => task_form } %>   <% end -%> </div> <%= add_task_link(form) %> <p>   <%= form.submit 'Save' %> </p>

And here's the partial that gets called above (_task.html.erb)

<div class="task">   <p>     <%= form.label :name %>     <%= form.text_field :name, :size => 15 %>     <%= remove_task_link( form ) %>   </p>   <%= observe_field :project_tasks_attributes_0_name, :function => 'alert("field changed")' %> </div>

This doesn't work because I need to change the observe_field name (:project_tasks_attributes_0_name) for each task. If I had 3 tasks, the then the observe_field names would be: :project_tasks_attributes_0_name :project_tasks_attributes_1_name :project_tasks_attributes_2_name

I was wondering if there is a way to access the index of the task form loop to generate field names like the above.

Thanks, Jay

Hi Jay,

OK, here's an example of what I'm trying to do.

_form.html.erb

<%= form.error_messages %> <p>   <%= form.label :title %><br />   <%= form.text_field :title %> </p> <p>   <%= form.label :due_date %><br />   <%= form.datetime_select :due_date %> </p> <div id="tasks">   <h3>Tasks</h3>   <% form.fields_for :tasks do |task_form| -%>     <%= render :partial => 'task', :locals => { :form => task_form } %>   <% end -%> </div> <%= add_task_link(form) %> <p>   <%= form.submit 'Save' %> </p>

And here's the partial that gets called above (_task.html.erb)

<div class="task">   <p>     <%= form.label :name %>     <%= form.text_field :name, :size => 15 %>     <%= remove_task_link( form ) %>   </p>   <%= observe_field :project_tasks_attributes_0_name, :function => 'alert("field changed")' %> </div>

This doesn't work because I need to change the observe_field name (:project_tasks_attributes_0_name) for each task. If I had 3 tasks, the then the observe_field names would be: :project_tasks_attributes_0_name :project_tasks_attributes_1_name :project_tasks_attributes_2_name

I was wondering if there is a way to access the index of the task form loop to generate field names like the above.

Good explanation. Thanks. A couple of suggestions re: approach. The first probably won't work but it would be interesting to know. I wonder if

do |task_form|

could be replaced with

each_with_index |task_form, i|

both do and each take blocks. Probably won't work, but...

If you're sure of the naming / order, then the easiest way would be something like (not tested)...

<% index = 0 %> <% form.fields_for :tasks do |task_form| -%> <%= render :partial => 'task', :locals => { :form => task_form, :index => index } %>   <% index += 1 %> <% end -%>

And then in the partial, using a string instead of a symbol...

<%= observe_field "project_tasks_attributes_#{index}_name" ...

Not terribly rubyish, I know, but something on that order should be workable :wink:

HTH, Bill

Hi Jay,

I've just run into exactly the same problem, as I'm using jQuery to build autocompleters and need the generated field id for my observe_field call. I've looked all over for a way to access the index, but couldn't find it anywhere. I decided to delve into the Rails code and found a couple of helper methods in the InstanceTagMethods moduke (http://github.com/rails/rails/blob/master/ actionpack/lib/action_view/helpers/form_helper.rb).

The methods of interest are santized_object_name and sanitized_method_name. They take the FormHelper's object_name (which is publically accessible) and return the generated form element id (e.g. person[attributes][0][name] becomes person_attributes_0_name). However, these methods are private to InstanceTagMethods, so you can't get at them from within your fields_for block.

The solution I've used is to duplicate these methods in my application_helper.rb, and call them from within my fields_for block:

# app/helpers/application_helper.rb def sanitized_object_name(object_name)   object_name.gsub(/\]\[|[^-a-zA-Z0-9:.]/,"_").sub(/_$/,"") end

def sanitized_method_nam(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

Then in my fields_for partial:

# 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 index partials (i.e. those with absolute indexes) and those generated with timestamp indexes (I'm using a customised version of Ryan Bates' latest nested form code for Rails 2.3 - http://github.com/ryanb/complex-form-examples). I'll post this code on my site aswell.

Hope this helps! If anyone knows a better way to access the form builder's index, please follow up this post!

Regards, Chris