Muhambi
Muhambi

Reputation: 3522

Issue with Nested Form rendering

I have a rails 4 app that has a form that looks like:

<%= form_for @store, :html => {:multipart => true} do |f| %>
  <%= f.fields_for :products do |product| %>
    <%= render partial: "edit_product_fields", locals: {product:product} %>
  <% end %>
  <%= f.submit %>
<% end %>

and the edit_product_fields partial looks like:

<select>
   <option value="Textbook" <% if product.type_of == "Textbook" %>selected<% end %>>Textbook</option>
   <option value="Magazine" <% if product.type_of == "Magazine" %>selected<% end %>>Magazine</option>
   <option value="Book"     <% if product.type_of == "Book"     %>selected<% end %>>Book</option>
</select>

When I have the code like this, I get the error:

undefined method `type_of' for #<NestedForm::Builder:0x00000102304f78>

but if I change the render line to (I just made it less explicit by taking out partial:):

<%= render "edit_product_fields", locals: {product:product} %>

I get the error:

undefined local variable or method `product' for #<#<Class:0x0000010235a248>:0x0000010684b3c0>

Upvotes: 0

Views: 44

Answers (2)

pdobb
pdobb

Reputation: 18037

Well type_of is not a ruby method. It looks like it's an attempt to type check though... But type checking is generally frowned upon in duck-typed languages. At any rate, we can take advantage of a Rails Form Helper instead and make this code a one-liner. But first...

The reason the render is complaining about undefined local variable or methodproduct'` after your change is because the render call expects either the first argument to be a partial name and the second to be a hash of locals:

<%= render "partial_name", local1: "a", local2: "b" %>

... or it expects the the whole thing to be a hash:

<%= render partial: "partial_name", locals: { local1: "a", local2: "b" } %>

Now, back to the form helper.

What you're looking for here is the select helper. This makes the following possible:

<%= product.select :type_of, %w[Textbook Magazine Book] %>

And this will render all of the same html as you've put into your _edit_product_field.html.erb partial. So you should probably just get rid of the partial to save on rendering time and put this directly into your form.

One other, minor point. It may be less confusing to call the fields_for block variable product_form instead of product. This is to help keep in mind that this is not a Product object you're dealing with, but rather a Form Generator object. Also, you don't need to specify :multipart => true in Rails 4 forms -- Rails knows to make the form multi-part if you have a file upload element within the form body.

So putting it all together:

<%= form_for @store do |f| %>
  <%= f.fields_for :products do |product_form| %>
    <%= product_form.select :product, %w[Textbook Magazine Book] %>
  <% end %>
  <%= f.submit %>
<% end %>

Upvotes: 2

Chris Peters
Chris Peters

Reputation: 18090

In your first code block, you have a Builder object being stored as product.

Fortunately, Builder provides an object method so you can access the actual object that it's representing in the form:

<select>
   <option value="Textbook" <%= 'selected' if product.object.type_of == "Textbook" %>>Textbook</option>
   <option value="Magazine" <%= 'selected' if product.object.type_of == "Magazine" %>>Magazine</option>
   <option value="Book"     <%= 'selected' if product.object.type_of == "Book"     %>>Book</option>
</select>

Upvotes: 2

Related Questions