Params hash is incorrect for nested arrays

Hi all,

I have a problem with a form for multiple models, but I am not sure if I have the right expectations for what "correct behaviour" should be. I am fairly new to rails so I would welcome any advice.

Say you have a form with fields for creating several contacts; each contact has fields for several phone numbers. The html might look like this:

<!-- first contact, 2 telephones --> <input name="contact[name]" ... /> <input name="contact[telephone][area_code]" ... /> <input name="contact[telephone][number]" ... /> <input name="contact[telephone][area_code]" ... /> <input name="contact[telephone][number]" ... />

The Contacts have not been created yet (they do not yet exist in the DB) so as there is no DB ID for the contacts, they will be stored in the params hash in a "contacts" array (as opposed to a "contacts" *hash*, which would be the case if the contacts had DB IDs). Same for the Telephones.

So this is what I'd expect in the params hash:

{ "contact" => [     { "name" => "nnn",       "telephone" => [         { "area_code" => "nnn", "number" => "nnn" },         { "area_code" => "nnn", "number" => "nnn" }       ]     }   ] }

After all, that is what the "telephone" array would look like if it wasn't nested inside "contact" -- i.e. with <input name="telephone [area_code]" /> etc.

Instead, I get something like this:

{ "contact" => [     { "name" => "nnn" },     { "telephone" => ["area_code" => "nnn"] },     { "telephone" => ["number" => "nnn"] },     { "telephone" => ["area_code" => "nnn"] },     { "telephone" => ["number" => "nnn"] }   ] }

Using hashes (when the contacts & telephones have IDs), instead of arrays, works correctly.

One way to get around this, that I can think of, is to create blank contacts & telephones in the DB before displaying the form. But then I'd have to make sure I deleted them if the user doesn't submit the form.

What it boils down to, is creating has_many :through associated records is a pain. :slight_smile:

Thanks in advance, Dave.

P.S. Forgot to mention I opened a ticket with an attached unit test: http://rails.lighthouseapp.com/projects/8994-ruby-on-rails/tickets/127

Dave Rothlisberger wrote:

<!-- first contact, 2 telephones --> <input name="contact[name]" ... /> <input name="contact[telephone][area_code]" ... /> <input name="contact[telephone][number]" ... /> <input name="contact[telephone][area_code]" ... /> <input name="contact[telephone][number]" ... />

So this is what I'd expect in the params hash:

{ "contact" => [     { "name" => "nnn",       "telephone" => [         { "area_code" => "nnn", "number" => "nnn" },         { "area_code" => "nnn", "number" => "nnn" }       ]     }   ] }

Instead, I get something like this:

{ "contact" => [     { "name" => "nnn" },     { "telephone" => ["area_code" => "nnn"] },     { "telephone" => ["number" => "nnn"] },     { "telephone" => ["area_code" => "nnn"] },     { "telephone" => ["number" => "nnn"] }   ] }

Um....

That is 'technically' the correct way for this to function.

Each one of the '' will add a new section onto the array, there is no way for the computer to 'know' which part of the array to push it into. So, it makes a new one every time it encounters a sign like ''

The easy way to fix this is to put a number in the like [1], which is the more appropriate way of doing this because it allows you to identify things easier.

Doing

<input name="contact[1][name]" ... /> <input name="contact[1][telephone][1][area_code]" ... /> <input name="contact[1][telephone][1][number]" ... /> <input name="contact[1][telephone][2][area_code]" ... /> <input name="contact[1][telephone][2][number]" ... />

ensures that rails will interpret the request properly because the second you do 2 contact's in 2 form its to ambiguous to know which names are associated with what and which telephones go where.

It doesn't make a new array for every when the arrays aren't nested: :slight_smile:

<input name="telephone[area_code]" ... /> <input name="telephone[number]" ... /> <input name="telephone[area_code]" ... /> <input name="telephone[number]" ... />

which generates

"telephone" => [         { "area_code" => "nnn", "number" => "nnn" },         { "area_code" => "nnn", "number" => "nnn" }       ]

I guess it knows to make a new array when it sees an attribute that is already in the array.

But you're right, once you start nesting the arrays it gets rather complicated. And your solution is fair enough, nice and simple. I might even use a position attribute for the key, or that might be over- complicating things.

Thanks! Dave.

I am having trouble with nested fields_for (this is on edge rails).

I want to accomplish the following:

<input name="contact[1][name]" ... /> <input name="contact[1][telephone][1][number]" ... /> <input name="contact[1][telephone][2][number]" ... />

My view code looks like:

form_for('contact', contact, :index => 1) do |c|   c.text_field(:name)   c.fields_for('telephone', telephone, :index => 1) do |r|     r.text_field(:number)   end end

(I am using :index because these records don't exist in the db yet, so don't have an id attribute to index with; I am incrementing this :index myself for each subsequent record).

This generates: <input name="contact[1][name]" ... /> <input name="contact[telephone][1][number]" ... />

i.e. "contact[1][name]" is correct, but the nested "contact[telephone][1][number]" is missing the id for "contact".

I've tried many variations to the fields_for parameters; for example, form_for('contact' ... ) and c.fields_for('telephone' ... ) generate <input name="contact[telephone][1][number]"> -- still missing the contact id, and now the [telephone] braces are all wack.

Am I doing something wrong? Is this a bug, or simply not possible?

At the moment I am contemplating rolling my own nesting by specifying the full path to each fields_for: fields_for "contacts[#{contact_position}][telephones]", telephone, :index => telephone_position

Any help greatly appreciated Dave Rothlisberger.