IAmJohn
IAmJohn

Reputation: 183

Nested form with has_many, only display one associated element

I am currently building a form for one of my models: Lead. A Lead is associated to several contacts. Thus, there is a has_many relation between Lead and Contact:

class Lead < ActiveRecord::Base
# ...
    has_many :contacts
# ...
end

I would like the form to only display one contact (The one that was created first), no matter how many contacts the lead is associated with.

I was only able to build my form so that it displays all the contacts associated to the lead, which is not the behavior I am expecting:

<%= f.fields_for :contacts do |contact_form| %>
  <%= render partial: '/contacts/form', locals: {f: contact_form} %>
<% end %>

I tried several things, like this for example (@primary_contact is initialized from the controller):

<%= f.fields_for @primary_contact do |contact_form| %>
  <%= render partial: '/contacts/form', locals: {f: contact_form} %>
<% end %>

But rails keeps rejecting the contact (Unpermitted parameters: contact) even though it should normally be accepted.

Does anybody know a trick to achieve that? I'm sure rails allows that somehow, but I could not find any way to do it. Thanks a lot to all of you!

Upvotes: 1

Views: 717

Answers (1)

nathanvda
nathanvda

Reputation: 50057

So your first way is the correct way to handled a nested form:

<%= f.fields_for :contacts do |contact_form| %>
  <%= render partial: '/contacts/form', locals: {f: contact_form} %>
<% end %>

Your second way, will only confuse rails, because while you know @primary_contact is a nested child, rails has no way to deduce that. To verify this you can check the naming of the fields in your html form, and the parameters that are posted to the controller. You will notice the naming is different, which causes rails to complain.

So, what you want is that only the first child is rendered, and thus editable. This seems a little weird, but that is not my problem I guess :)

The simple approach is to only render the first child, with a counter or a flag, something like:

<%- first_rendered = false %>
<%= f.fields_for :contacts do |contact_form| %>
  <%- unless first_rendered do %>
    <%= render partial: '/contacts/form', locals: {f: contact_form} %>
    <%- first_rendered = true %>
  <%- end %>
<% end %>

So, for education purposes I am going to show this in haml as well, which is much more readable:

- first_rendered = false
= f.fields_for :contacts do |contact_form|
  - unless first_rendered do 
    = render partial: '/contacts/form', locals: {f: contact_form} 
    - first_rendered = true 

Now this code would be much simpler if you would just ensure that the lead has only one contact (why only show/edit one contact and allow there to be more?).

Upvotes: 2

Related Questions