I am working on an invoice tracking system for my business and have gotten so close to getting this portion to work!
I have an Invoice model with many InvoiceItems.
I am following this example (which is based off of ryan daigle's post) http://transfs.com/devblog/2009/06/26/nested-forms-with-rails-2-3-helpers-and-javascript-tricks/ and I've looked at a couple of other similar examples as well.
I can generate the fields just fine, but when I try to add more fields and save, I get the following message:
ActiveRecord::UnknownAttributeError in InvoicesController#update
unknown attribute: invoice_items
Parameters: {"commit"=>"Submit", "invoice"=>{"invoice_items_attributes"=>{"1248641854778"=> {"invoice_items"=>{"project_id"=>"9", "_delete"=>"0", "amount_billed"=>"48"}}, "0"=>{"project_id"=>"8", "_delete"=>"0", "id"=>"29", "amount_billed"=>"32.00"}}, "date_due(1i)"=>"2009", "date_due(2i)"=>"8", "date_due(3i)"=>"9"}, "client_id"=>"1", "_method"=>"put", "authenticity_token"=>"WiwqytxYy/KZSrO76kWKGCH2hMlgiCj1lSedQV3h6KA=", "id"=>"17"}
The second line of parameters looks like it goes one level too deep with the Hash key "invoice_items". If you go further down to where the project_id is 8, that is the record that was already in existance. Below is the yaml output of the request.
Here is the same request converted to yaml:
--- !map:HashWithIndifferentAccess commit: Submit invoice: !map:HashWithIndifferentAccess invoice_items_attributes: !map:HashWithIndifferentAccess "1248641854778": !map:HashWithIndifferentAccess invoice_items: !map:HashWithIndifferentAccess project_id: "9" _delete: "0" amount_billed: "48" "0": !map:HashWithIndifferentAccess project_id: "8" _delete: "0" id: "29" amount_billed: "32.00" date_due(1i): "2009" date_due(2i): "8" date_due(3i): "9" authenticity_token: WiwqytxYy/KZSrO76kWKGCH2hMlgiCj1lSedQV3h6KA= _method: put client_id: "1" action: update id: "17" controller: invoices
My form basically looks like this:
<% form_for [@client, @invoice], setup_invoice(@invoice) do |f| %> <%= f.error_messages %>
<h2>Add Projects:</h2> <ul id="invoice_items"> <li class="invoice_item">
<% f.fields_for :invoice_items do |invoice_item| %> <%= invoice_item.collection_select :project_id, @client.projects, :id, :title, { :include_blank => true } %> <%= invoice_item.label :amount_billed, "Amount to bill" %> <%= invoice_item.text_field :amount_billed %>
<% if invoice_item.object.new_record? %> <%= link_to_function "clear", "this.up('.invoice_item').remove ()" %> <% else %> <%= invoice_item.check_box '_delete' %> <%= invoice_item.label '_delete', 'Remove' %> <% end %> <% end %>
</li> </ul> <%= link_to_new_nested_form "Add a Project", f, :invoice_items %> <hr /> <p> <%= f.label :date_due %><br /> <%= f.date_select :date_due, :default => 15.days.from_now, :order => [:day, :month, :year], :start_year => Time.now.year
%> </p> <p><%= f.submit "Submit" %></p> <% end %>
I'm just not sure how to change the code to make it work!
Here is what I have in my application_helper.rb:
def generate_html(form_builder, method, options = {}) options[:object] ||= form_builder.object.class.reflect_on_association(method).klass.new options[:partial] ||= method.to_s.singularize options[:form_builder_local] ||= :f form_builder.fields_for(method, options[:object], :child_index => 'NEW_RECORD') do |f| render(:partial => options[:partial], :locals => { options [:form_builder_local] => f }) end end
def link_to_new_nested_form(name, form_builder, method, options = {}) options[:object] ||= form_builder.object.class.reflect_on_association(method).klass.new options[:partial] ||= method.to_s.singularize options[:form_builder_local] ||= :f options[:element_id] ||= method.to_s options[:position] ||= :bottom link_to_function name do |page| html = generate_html(form_builder, method, :object => options[:object], :partial => options[:partial], :form_builder_local => options [:form_builder_local] ) page << %{ $('#{options[:element_id]}').insert({ #{options [:position]}: "#{ escape_javascript html }".replace(/NEW_RECORD/g, new Date().getTime()) }); } end end
I've had other problems with this approach as well, but if I can at least get it to save to the db I should be okay.