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