Dynamic form fields

Hi, i know is a very common problem this one but i have been searching
for information about the best way (rails way) of doing this and i
haven’t met a good solution:

I have a model named Contact that has many phone numbers. I want to
have a form that dynamically add/delete 2 fields: phone number and a
category (home, personal, work, etc). I’m doing this with RJS, no
problem with that.

The question would be, is there a way of naming the fields and use
form_for :contact and send and hash/array of these lines and add them
as new relationships.

I read about parsing something like contact[phones][][number] but
haven’t found much more info.

Thanks you in advance!

Emilio T. wrote:

The question would be, is there a way of naming the fields and use
form_for :contact and send and hash/array of these lines and add them
as new relationships.

I read about parsing something like contact[phones][][number] but
haven’t found much more info.

This is something I struggled with for a little while on the first Rails
app I built back with Rails pre-1.0 - it seemed like the path of least
resistance has always involved treating each relationship add/edit as a
separate event, updating the model immediately. However, what I really
wanted to do was to have a form where you could edit the master record
and some associated details at the one time, adding and deleting detail
records as needed on the form, but only saving all changes back to the
database when the user submitted.

In the end I managed to come up with a solution that works quite well
for me - well enough that I haven’t really checked to see if there is a
better way of doing this with the current Rails release, so my solution
may be able to be refined further… anyway, here is what I came up
with:

View
On the view side in the form, you’ll have the master fields for your
Contact object in the usual fashion, and then name the detail object
fields for the number and contact after the association on the master
object. Thus if Contact has_many :phone_numbers, I would name my detail
object fields as phone_number[0][number], phone_number[0][category],
phone_number[1][number], etc. In truth, the numbers don’t matter - they
only exist to uniquely identify each detail record within the form
submission.

Adding/Deleting Detail Records
This can be done a couple of different ways - I’ve used both RJS
rendering a partial with a session variable keeping track of the next
number to use, and javascript in the browser to insert/delete detail
records. I prefer the latter approach now, but it requires writing
javascript rather than using RJS to shield you from it.

Controller
Most of the difficulty was in the controller, since I needed a way to
process all the edits in one create/update action, and I wanted a way to
do this that could be re-used on different forms. I ended up writing a
module to handle this logic in a generic fashion, and now just include
it into my Application controller base class when the same need arises.

The module defines a single save method that takes a couple of
parameters: the name of the param that defines the master object values
(e.g. contact), and the name (or names) of the object(s) in the params
hash that define the values for each instance of the detail class(es):
phone_number in this example. (Note though that you can for example
create multiple detail objects for several different associations at
once if you need to - such as a contact with many phone numbers and many
email addresses).

By convention, I require the param keys to be named after the
association on the master object, so that I can use
reflect_on_associations to find all the other info I need to
create/update the detail objects. It’s then a matter of
locating/updating or creating both the master and all the detail
records, and keeping a track of any detail records that may need to be
deleted if they are not also in the submitted params. All of this is
done inside a transaction, so that if a validation error occurs on say a
detail attribute, the whole save can be rolled back.

Anyway, that’s an explanation of how the code works - if you’d like a
copy, I’d be happy to email it to you.

Cheers,

Adam