Rails -- fields_for not working?

Kvass picture Kvass · Aug 16, 2011 · Viewed 15.4k times · Source

When I run the rails server and go to the school/new page on my site, the field with the label "school" where I can enter the school's name appears, but all the other fields under fields_for which are for entering the school administrator's info do not show up on my site -- when I use "inspect element" on my form it is like they are not even there. Why aren't they appearing on my page?

<%= form_for(@school) do |f| %>
  <% if @school.errors.any? %>
    <div id="error_explanation">
      <h2><%= pluralize(@school.errors.count, "error") %> prohibited this school from being saved:</h2>

      <ul>
      <% @school.errors.full_messages.each do |msg| %>
        <li><%= msg %></li>
      <% end %>
      </ul>
    </div>
  <% end %>

  <div class="field">
    <%= f.label :school %><br />
    <%= f.text_field :name %>
  </div>
  <%= f.fields_for :admin do |f2| %>
    <div class="field">
      <%= f2.label "administrator name" %><br />
      <%= f2.text_field :name %>
    </div>
    <div class="field">
      <%= f2.label "administrator email" %><br />
      <%= f2.text_field :email %>
    </div>
    <div class="field">
      <%= f2.label "administrator gender" %><br />
      <%= f2.text_field :gender %>
    </div>
  <% end %>
  <div class="actions">
    <%= f.submit %>
  </div>
<% end %>

Answer

Nathan Wallace picture Nathan Wallace · Mar 31, 2014

TLDR:

For my purposes, I had to make sure the form object's child association was populated with a new record. Originally, the parent object's child association returned nil. I just had to instantiate an object and the form rendered correctly.


In case anyone arrives here from Google, I had a the same problem as the poster, but the accepted answer didn't help me.

First, some background. When you use the plain fields_for instead of f.fields_for, the fields are rendered differently. The plain version (as suggested above) does not associate the fields rendered with the form's object. Look at the different outputs:

fields_for

form_for @parent_instance do |f|
  f.text_field :parent_field
  fields_for :child_obj do |child_fields|
    child_fields.text_field :child_field
  end
end

Renders

<input id="parent_class_parent_field" name="parent_class[parent_field]" type="text" />
<input id="child_class_child_field" name="child_class[child_field]" type="text" />

Which Rails will parse into these parameters:

{ "parent_class" => {
      "parent_field" => "Parent field value"
    },
  "child_class" => {
      "child_field" => "Child field value"
    }
}

f.fields_for

form_for @parent_instance do |f|
  f.text_field :parent_field
  f.fields_for :child_obj do |child_fields|
    child_fields.text_field :child_field
  end
end

Renders

<input id="parent_class_parent_field" name="parent_class[parent_field]" type="text" />
<input id="parent_class_child_class_child_field" name="parent_class[child_class][child_field]" type="text" />

Which Rails will parse into these parameters:

{ "parent_class" => {
      "parent_field" => "Parent field value",
      "child_class" => {
          "child_field" => "Child field value"
        }
    }
}

The difference is that the child object's parameters only get nested under the parent when you use f.fields_for. So using the accepted answer's suggestion of using the bare fields_for didn't work for creating an associated record.

Turned out, the reason f.fields_for wasn't rendering anything was because the child association was nil. To fix it and have my form render correctly and return the parameters in the properly nested way, I had to instantiate that association like this in my controller:

def new
  @parent = ParentClass.new
  @parent.child_obj = ChildClass.new
end