Dynamic form elements via AJAX

Matthew Isleb wrote:

I'm trying to build a form with an ordered list of elements. You can add/remove an arbitrary number of items. I can use link_to_remote to insert a new element, but I am having problems maintaining order when it comes to submitting the form. Here is my "element":

<li id="<%= "choice_#{i}" %>">
  <%= text_field_tag "choice_#{i}[text]", choice[0], :size => 16, :id => "choice_#{i}_text" %>
  <%= text_field_tag "choice_#{i}[value]", choice[1], :size => 4, :id => "choice_#{i}_value" %>
  <%= link_to_remote '+', :url => {:action => 'insert_choice', :id => "choice_#{i}" } %>
  <%= link_to_function '-', "Element.remove('choice_#{i}')" %>
</li>

It is actually a set of two text_fields, as you can see. When the form is initially built with a default of 3 "choices," the order is given by choice_0, choice_1, etc. But when insert a new "choice" <li> via ajax it doesn't have a proper name/id. I can call it "choice_new" or something but when the form is submitted the controller won't know what order they were in the form.

I'd recommend changing to

<% @choices = Array.new(3, Choice.new) if @choices.empty %>
<ul>
<% @choices.each do |choice| %>
<li>
<%= text_field_tag 'choice_text[]', choice.text, :size => 16 %>
<%= text_field_tag 'choice_value[]', choice.value, :size => 4 %>
<%= link_to_function '+', 'var c=this.parentNode;c.parentNode.insertBefore(c.cloneNode(true),c)' %>
<%= link_to_function '-', 'var c=this.parentNode;c.parentNode.removeChild(c)' %>
</li>
<% end %>
</ul>

Now there's no numbering or ids to maintain, the texts and values
are each posted as arrays, respecting their order in the document,
the client-side insertion and deletion of choices is much more
responsive for the user, and you no longer need to include Prototype.

However for efficiency in actual implementation the scripts
should be made page-wide functions that are called from each link.