Question

In my app, I have Contacts, which have many Goals (Goals belong to Contact). I use a nested form to create/update data for both models, however only the first child Goal gets saved during create/update actions, which can be seen from the parameters being passed below:

Started PATCH "/contacts/2" for 127.0.0.1 at 2014-05-11 19:10:40 -0400
Processing by ContactsController#update as HTML
  Parameters: {"utf8"=>"✓", "authenticity_token"=>"wKgOPegIJOFyrrLAnbdD67qqATFXrPNqMIT7I/tpNfo=", "contact"=>{"name"=>"Steven Tyler", "title"=>"Manager", "company"=>"Dell", "email"=>"steve@dell.com", "notes"=>"cool guy", "goals_attributes"=>{"0"=>{"title"=>"goal1", "due_date(1i)"=>"2014", "due_date(2i)"=>"5", "due_date(3i)"=>"11", "notes"=>"fdfsd", "_destroy"=>"false", "id"=>"13"}}}, "commit"=>"submit", "id"=>"2"}
  Contact Load (0.1ms)  SELECT  "contacts".* FROM "contacts"  WHERE "contacts"."id" = ? LIMIT 1  [["id", 2]]
   (0.1ms)  begin transaction
  Goal Load (0.3ms)  SELECT "goals".* FROM "goals"  WHERE "goals"."contact_id" = ? AND "goals"."id" IN (13)  [["contact_id", 2]]
   (0.1ms)  commit transaction
Redirected to http://localhost:3000/contacts/2

Here are my Models:

class Contact < ActiveRecord::Base

  belongs_to :user
  has_many :goals, dependent: :destroy
  accepts_nested_attributes_for :goals, allow_destroy: true, reject_if: lambda {|attributes| attributes['title'].blank? && attributes['notes'].blank?}
  validates_presence_of :user_id,:name,:title,:email
end

class Goal < ActiveRecord::Base

  belongs_to :contact
  validates_presence_of :title, :due_date
end

And, strong parameters in the Contacts controller:

def contact_params
      params.require(:contact).permit(:name, :title, :company, :email, :notes, goals_attributes: [:title, :due_date, :notes, :contact_id, :_destroy, :id])
    end

Here are the two forms used: https://gist.github.com/nowgeez/5fbe99e814330c1b371c Any idea why only the first Goal is being saved and not any other subsequent goals created in the form? Thanks in advance for the help.

Was it helpful?

Solution

As stated in the documentation, it is important to use the correct nesting of divs.

So in your case you should write:

<%= simple_form_for(@contact) do |f| %>

  <%= f.input :name %>
  <%= f.input :title %>
  <%= f.input :company %>
  <%= f.input :email %>
  <%= f.input :notes %>

  <h3>Goals:</h3>
  <div id="goals">
    <%= f.simple_fields_for(:goals) do |goal| %>
      <%= render 'goal_fields', :f => goal %>
    <% end %>  
    <div class="links">          
      <%= link_to_add_association 'add goal', f, :goals %>
    </div>
  </div>

  <%= f.submit :submit %>

<% end %>

For clarity I have removed the error-block. The goals themselves should be wrapped in a div, for the remove to work.

<div class="nested-fields>
  <%= f.input :title %>
  <%= f.input :due_date %>
  <%= f.input :notes %>

  <%= link_to_remove_association "remove goal", f %>
</div>

On a side-note: I hate to write erb, it is much more verbose, and from your gist it seems properly nested, but it actually is not. Haml is imho much clearer. Just to prove my point, the same as the above form in haml looks like:

= simple_form_for @contact do |f|
  = f.input :name
  = f.input :title
  = f.input :company
  = f.input :email
  = f.input :notes

  %h3 Goals
  #goals
    = f.simple_fields_for :goals do |goal|
      = render 'goal_fields', :f => goal
    .links
      = link_to_add_association 'add goal', f, :goals
  = f.submit

But I understand it is a matter of taste.

OTHER TIPS

In your request there is only one goal

"goals_attributes"=>{"0"=>{"title"=>"goal1", "due_date(1i)"=>"2014", "due_date(2i)"=>"5", "due_date(3i)"=>"11", "notes"=>"fdfsd", "_destroy"=>"false", "id"=>"13"}}}

I guess you are doing something wrong with strong parameters, change it to something like this:

def contact_params
  params.require(:contact)
  .permit(:name, :title, :email, :company, :notes,
   goal_attributes: [:title, :due_date, :notes, :_destroy, :id])
end
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top