Re: Creating Variable Number of Models in One Form

What if I need to add auto-complete power to all the new textboxes?

I tried but I just don't know how to refer to the each textboxes using a symbol:


_register_fields.rhtml

<div id="register_<%= index %>">
<li>
<% fields_for "registers[#{index}]", register do |r| %>
    <p>
        <%= r.text_field :ref, :size => '3' %> <%= link_to_remote 'eliminar', :url => { :action => 'remove_register', :index => index } %>
        <img id="registers[#{index}]_spinner" src='/images/wait18trans.gif' style='display:none;'/>
        <div class="auto_complete" id="registers[#{index}]_ref_auto_complete" ></div>
        <%= auto_complete_field :"registers_0_ref",
                                                         :url => { :action => 'autocomplete_register' },
                                                         :indicator => "registers[#{index}]_spinner" %>
    </p>   
<% end %>
</li>
</div>

102

Re: Creating Variable Number of Models in One Form

midwaltz wrote:

One last thing, Highrise was released a few months ago (way after 1.2.3 release), and was very probably built with edge rails. I was wondering if edge has specific functionalities for this that 1.2.3 doesn't. Could it be required to jump to edge to be able to profit from this approach ?

Yep. Notice the "[]" in the middle of the text field name in their code. This doesn't work in 1.2.3 which is why I need to pass the index. I haven't tried it in edge rails so maybe it works there. I plan to revisit this once 2.0 is released. Hopefully there will be a better way to handle it.

Railscasts - Free Ruby on Rails Screencasts

Re: Creating Variable Number of Models in One Form

ryanb wrote:

Notice the "[]" in the middle of the text field name in their code. This doesn't work in 1.2.3 which is why I need to pass the index.

Actually, as I briefly mentioned in my last post, that specific feature does work. It's possible to put multiple statements like this:

fields_for "parent[child][]"

or even this:

fields_for "parent[child][][name]"

It successfully reads them in order when creating the input boxes. And when the form is saved, instead of passing an array of hashes (if we had passed an index), it passes a hash of hashes. The problem is saving these params directly into the parent model, doing directly @parent = Parent.new(params[:parent]) would result in a HashWithIndifferentAccess error); it's probably necessary to reorganize or recreate that nested collection before saving. But at least it might bring some flexibility at the template level, since there'd be no more need to pass the array index. The outcome is still theory to me, since I haven't put it in practice yet.

Edge might be optimized for this approach, though. But I'd rather not venture there so soon...

104

Re: Creating Variable Number of Models in One Form

Wow, you're right. I didn't think "[]" would work in the middle of the name (I knew it did at the end). At least it didn't work when I wrote this article originally as I had attempted that approach. This will certainly lead to a cleaner solution. I'll have to play around with it for a bit. Thanks.

Railscasts - Free Ruby on Rails Screencasts

Re: Creating Variable Number of Models in One Form

Great, looking forward to see the new tutorial. big_smile

I've been playing a bit with it today, and I found out it's the reverse of what I wrote in my previous post. If indexes are passed in all inputs for that collection, the collection will be sent as a hash of hashes in the params. If no index is passed for any input, the collection will be sent as an array of hashes. And, if it's mixed (if some inputs have the index passed and some don't), rails blows up with a 500 internal server error. This is probably something that is fixed on edge, since in the Highrise example it's mixed.

My solution was to never pass an index, but my case is very specific, I just wanted to set multiple associations with already created rows. But in case you do need the index to keep track of the row, I suppose a hidden field with the id value would do. Curiously, they use that in Highrise. Another thing worth mentioning is that I had to work with an independent collection, to make sure no index was passed.

106

Re: Creating Variable Number of Models in One Form

midwaltz wrote:

And, if it's mixed (if some inputs have the index passed and some don't), rails blows up with a 500 internal server error. This is probably something that is fixed on edge, since in the Highrise example it's mixed.

Nope, you'll get the same error on edge rails. Not sure what Highrise is doing.

What's really bad about this is Rails will automatically add the id inbetween the brackets for models which have an id. This means if you try to do this:

  <% for task in @project.tasks %>
    <% fields_for "project[task_attributes][]", task do |t| %>
      <%= t.text_field :name %>
    <% end %>
  <% end %>

And you have some tasks which have ids and some don't (because they're new) then they will end up mixed like this:

<input id="project_task_attributes_9_name" name="project[task_attributes][9][name]" size="30" type="text" value="Foo" />
<input id="project_task_attributes__name" name="project[task_attributes][][name]" size="30" type="text" />

No clue why they do that. It would be much better to pass the id as a hidden field instead.

Railscasts - Free Ruby on Rails Screencasts

107

Re: Creating Variable Number of Models in One Form

Found out you can disable the auto index insertion by passing the :index option when you generate the text field:

<%= t.text_field :name, :index => nil %>

Not the best solution. Maybe there's a way to do it through a form builder.

Railscasts - Free Ruby on Rails Screencasts

Re: Creating Variable Number of Models in One Form

ryanb wrote:
midwaltz wrote:

And, if it's mixed (if some inputs have the index passed and some don't), rails blows up with a 500 internal server error. This is probably something that is fixed on edge, since in the Highrise example it's mixed.

Nope, you'll get the same error on edge rails. Not sure what Highrise is doing.

Damn.. maybe it's a plugin?

ryanb wrote:

What's really bad about this is Rails will automatically add the id inbetween the brackets for models which have an id.

Exactly... In my case I had to use an intermediary array, full of model objects without ids. To edit a model previously created, I clone it's attributes into a new model object (new_record) inside that array, to make sure no id is passed....!

Re: Creating Variable Number of Models in One Form

ryanb wrote:

Found out you can disable the auto index insertion by passing the :index option when you generate the text field:

wow, good catch.. now I might rethink a few things in my code ..

110

Re: Creating Variable Number of Models in One Form

Does anyone know how to make another dynamic partial in the already dynamic partial "task_fields"? And of course with a form in it so it would be saved in the database.

Are you happy now?

Re: Creating Variable Number of Models in One Form

I'm trying to build a form with variable number of models, but haven't quite got it sorted. Summary of the issue: I am trying to get data into a "form_data" model from a form where the number of records to update is determined by the number of records in another model ("questions"). I get an "undefined method `answer' for #<Question:0x354feb8>" error and no data in form_data unless I add a dummy field "answer" to the questions table.

The background: There are form_templates (e.g. questionnaires) and related questions. The form_template model basically contains header information and acts as a parent to the questions. There is an arbitrary number of questions on a form_template. We want to be able to administer a form_template many times and change the questions over time, etc. So, there is a form_actuals table that holds the header information for an instance of a "real" form_template, and there is a form_data table that holds the answers to the questions for a given form_actual filled out by a person. A form_template can have many form_actuals; likewise a question can have many form_data.

So,
Definitions: form_templates --< questions
Instances: form_actuals --< form_data

To get the information into the database I want to be able to set up the definitions (form_templates and questions) and then enter the actual responses (form_actuals and form_data).


The view (simplified):

<% form_for :form_actual, :url => { :action => :create } do |form| %>
   
     <table>
       <% @questions.each_with_index do |@question, index| %>
         <% fields_for "form_data[#{index}]", @question do |f| %>
           <%= f.hidden_field "question_id", :value =>  @question.id %>
           <%= f.hidden_field "question_text", :value =>  @question.question %>
   
           <tr>
            <td><%= @question.question_number.to_s %></td>
            <td><%=h @question.question %></td>
            <td><%= f.text_field("answer") %></td>
          </tr>
 
        <% end %>
      <% end %>
    </table>
 
  <% end %>

The form_actuals controller:

def create
  @form_actual = FormActual.new(params[:form_actual])
  params[:form_data].each_value { |form_data| @form_actual.form_data.build(form_data) }

   # If the 'Save' button is clicked.
   if params[:save_button] && @form_actual.save
    etc...
end


So, I start by iterating through the questions (line 4) and then creating fields for form_data (line 5) based on them. I'm passing the id and text of a question to a form_data record (lines 6 and 7) so that they are stored on a form_data record (if a question is modified, it should not affect previously entered form_data). Then I display the question number and text on the form (lines 10 and 11) and set up a field for the user to enter the answer (line 12).

The catch is this: "Answer" is the field in form_data that stores an actual response to a question, but I get an "undefined method `answer' for #<Question:0x354feb8>" error unless I add a dummy field "answer" to the questions table. If there is a field "answer" in questions, then the form is created successfully and correctly (with indexed fields) and the controller does its magic to parse the form into separate records in form_data.

This seems odd and I'm wondering if I have something fundamentally wrong here. I'm using Rails 1.2.3, btw. Thanks for any insight, and apologies for the length of this post. I've cross-posted this to Ruby on Rails Talk, but haven't had any responses there yet.

112

Re: Creating Variable Number of Models in One Form

I'll be rewriting these tutorials regarding multiple models in one form shortly. In the meantime I've started a series of screencasts showing this new technique. You can find the first one here

Railscasts - Free Ruby on Rails Screencasts

Re: Creating Variable Number of Models in One Form

beautiful, simply beautiful. thanks. my code is now almost as clean as a fresh scaffold. smile
I just put a comment on your screencast too.

still don't know how they managed to mix indexed and non-indexed fields in Highrise, but who cares, this is close enough, I simply pass :index => nil in the dynamic fields.

thanks again.

Re: Creating Variable Number of Models in One Form

First off, I'm rather new to rails.

Lets say that Projects had a decimal value in it and Tasks did as well.
And when on the new rhtml page I wanted to add all of those values together...how would you go about doing that? Each time you add a new task the value displayed in new would be updated.

115

Re: Creating Variable Number of Models in One Form

Is it similar to a cart system where when you add items the total updates instantly? If so I recommend reading the RJS Templates for Rails book. There's an example in there that does exactly that. You may need to do a few things to apply it to this tutorial, but it should get you started.

Railscasts - Free Ruby on Rails Screencasts

Re: Creating Variable Number of Models in One Form

ryanb wrote:

Is it similar to a cart system where when you add items the total updates instantly? If so I recommend reading the RJS Templates for Rails book. There's an example in there that does exactly that. You may need to do a few things to apply it to this tutorial, but it should get you started.

so we will wait for a written tutorial here that talk about creating multiple models in one form (and also the editing part and validations) only with client javascript like in your screencast....but i really don't like screencasts to study and reference i like to read...so pdf etc wink

Re: Creating Variable Number of Models in One Form

it would be nice also to have a tutorial on how to create 3 models in one time/page/form. A main model that has some other model associated with it....and this secondary model has another model associated with it.

Re: Creating Variable Number of Models in One Form

I'm working on a multi-model form and have ground to a complete halt.

I'm using a fields_for with an object_name of: project[team_props][][team_association_props][login_account_props][contact_mechanism_props]

The issue is that everything after the [team_association_props] is completely lost in the request parameters:

{"project"=>{"name"=>"",
"product_owner_props"=>{"contact_mechanism_props"=>{"first_name"=>"",
"last_name"=>""},
"email_address"=>""},
"scrum_master_props"=>{"contact_mechanism_props"=>{"first_name"=>"",
"last_name"=>""},
"email_address"=>""},
"team_props"=>[{"team_association_props"=>{}},
{"team_association_props"=>{}},
{"team_association_props"=>{}},
{"team_association_props"=>{}},
{"team_association_props"=>{}},
{"team_association_props"=>{}},
{"team_association_props"=>{}},
{"team_association_props"=>{}},
{"team_association_props"=>{}},
{"team_association_props"=>{}},
{"team_association_props"=>{}},
{"team_association_props"=>{}},
{"team_association_props"=>{}},
{"team_association_props"=>{}},
{"team_association_props"=>{}}],
"description"=>""},
"__checkbox_scrumOfOne"=>"true",
"billing_account_id"=>"aMRblGKlCr3jLlabBlOzbT"}

Now, if I do the following: project[team_props][#{index}][team_association_props][login_account_props][contact_mechanism_props]

Making the array into a Hash I get the following in the request:

{"project"=>{"name"=>"",
"product_owner_props"=>{"contact_mechanism_props"=>{"first_name"=>"",
"last_name"=>""},
"email_address"=>""},
"scrum_master_props"=>{"contact_mechanism_props"=>{"first_name"=>"",
"last_name"=>""},
"email_address"=>""},
"team_props"=>{"0"=>{"team_association_props"=>{"login_account_props"=>{"contact_mechanism_props"=>{"first_name"=>"",
"last_name"=>""},
"email_address"=>""}}},
"1"=>{"team_association_props"=>{"login_account_props"=>{"contact_mechanism_props"=>{"first_name"=>"",
"last_name"=>""},
"email_address"=>""}}},
"2"=>{"team_association_props"=>{"login_account_props"=>{"contact_mechanism_props"=>{"first_name"=>"",
"last_name"=>""},
"email_address"=>""}}},
"3"=>{"team_association_props"=>{"login_account_props"=>{"contact_mechanism_props"=>{"first_name"=>"",
"last_name"=>""},
"email_address"=>""}}},
"4"=>{"team_association_props"=>{"login_account_props"=>{"contact_mechanism_props"=>{"first_name"=>"",
"last_name"=>""},
"email_address"=>""}}}},
"description"=>""},
"__checkbox_scrumOfOne"=>"true",
"billing_account_id"=>"aMRblGKlCr3jLlabBlOzbT"}

Like I would expect.

My question is am I just doing this wrong? Or is Rails deficient in this use case?

Thanks,

Mark

Re: Creating Variable Number of Models in One Form

I'm also running into the same scenario when I have a complex model where a hash array contains another hash array. All the indexes to the arrays have been removed using :index => null but when the form is submitted, the second set of has arrays is all all empty.

<% fields_for "budget[amount_attributes][][subamount_attributes][]", new_subamount do |g| %>
<%= g.text_field "amount",  :index => nil,:size => 12, :maxlength => 12 %>
<%= g.hidden_field "budget_amount_source_type_id" ,:index => nil %>

is producing this HTML:

<input id="budget_amount_attributes____subamount_attributes__amount" 
maxlength="12" name="budget[amount_attributes][][subamount_attributes][][amount]"
size="12" type="text">

<input id="budget_amount_attributes____subamount_attributes__budget_amount_source_type_id"
name="budget[amount_attributes][][subamount_attributes][][budget_amount_source_type_id]"
type="hidden">


When the form is submitted:

"budget"=>{"amount_attributes"=>[{"purpose"=>"For my people",
"should_destroy"=>"",
"amount"=>"100.00",
"category_id"=>"1",
"amount_id"=>"1"},
{"purpose"=>"XXX",
"should_destroy"=>"",
"amount"=>"1.00",
"category_id"=>"1",
"amount_id"=>"5"},
{"purpose"=>"z",
"should_destroy"=>"",
"amount"=>"1.00",
"category_id"=>"1",
"amount_id"=>"13"},
{"purpose"=>"sdad",
"should_destroy"=>"",
"amount"=>"32312.00",
"category_id"=>"2",
"amount_id"=>"18"},
{"purpose"=>"xx",
"amount"=>"xx",
"category_id"=>"2",
"subamount_attributes"=>[]},
{"subamount_attributes"=>[]},
{"subamount_attributes"=>[]},
{"subamount_attributes"=>[]},
{"subamount_attributes"=>[]},
{"subamount_attributes"=>[]}]}

It seems like the array counter for the amount_attributes is getting lost when re-constructing what the form submitted. The subamount_attributes values are not getting picked up.

This is driving me nuts. It looks like there's a bug open for it and jdrowell and lastobelus are trying to fix it and I'm cheering them on!! http://dev.rubyonrails.org/ticket/9030

It's a nested model problem and not just because there's two []s. Any 4th level attribute will not show up: a[b][][c] is ok but once you go down one more level a[b][][c][d] or a[b][][c][], you're screwed. This seems like a major defect.

Last edited by athu9999 (2007-11-18 14:56:28)

Re: Creating Variable Number of Models in One Form

Thanks for the help! I was getting the HashWithIndifferentAccess error when my address associated with another model was being created but with the code from here it was easily fixed. Instead of making address_attr the field though, I just added this in the Model that has an address.

def address=(attributes)
  build_address if address.nil?
  address.attributes = attributes
end