views:

76

answers:

3

I'm attempting to use the fields_for command to combine two models edit functions. I'm editing several variables. With these variables I'd like to include some basic information that is associated with the model, such as @line_item.inventory.item. The only way I could accomplish this is by creating a double loop which doesn't work for obvious reasons. Is there a way to pass two arguments into a for loop?

ie. fields_for :line_items & @order.line_items do ???

<% f.fields_for :line_items do |f| %>
<% for line_item in @order.line_items do %>


    <td><%= line_item.inventory.item  %></td>
    <td><%= f.text_field :inventory_id, :size => 3 %></td>
    <td><%= line_item.inventory.unit2_id  %></td>
    <td><%= line_item.inventory.catalognumber  %></td>
    <td><%= f.text_field :quantity, :size => 3 %></td>
            <td> <%= f.text_field :item_price, :size => 3 %></td>
            <td><%= f.text_field :total_price, :size => 3 %></td>
    <td><%= f.check_box :received %><b>Received</b> </td>
    <td><%= f.text_field :notes %></td>
     <td><%= link_to 'remove item', line_item, :confirm => 'Are you sure?', :method => :delete %></td>

</tr>
<% end %>
<% end %>
A: 

I'm not sure there is really a way for this, since the fields_for block and the for loop are basically 2 different things. So it's not really a "double loop".

nowk
+2  A: 

You should look at accepts_nested_attributes_for. When used correctly, it can solve your problem.

Assuming the encompassing form is for an order you want to add the following to the Order model, if it isn't already there.

class Order < ActiveRecord::Base
  has_many :line_items
  accepts_nested_attributes_for :line_items
end

And the view:

<% form_for :order do |f| %>
  ... 
    Order specific fields
  ...
<% f.fields_for :line_items do |line_item_form| %>
<% line_item = line_item_form.object


    <td><%= line_item.inventory.item  %></td>
    <td><%= line_item_form.text_field :inventory_id, :size => 3 %></td>
    <td><%= line_item.inventory.unit2_id  %></td>
    <td><%= line_item.inventory.catalognumber  %></td>
    <td><%= line_item_form.text_field :quantity, :size => 3 %></td>
            <td> <%= line_item_form.text_field :item_price, :size => 3 %></td>
            <td><%= line_item_form.text_field :total_price, :size => 3 %></td>
    <td><%= line_item_form.check_box :received %><b>Received</b> </td>
    <td><%= line_item_form.text_field :notes %></td>
     <td><%= link_to 'remove item', line_item, :confirm => 'Are you sure?', :method => :delete %></td>

</tr>
<% end %>

<%end%>

fields_for, when used with accepts_nested_attributes_for and given an association, will loop through all items already associated with the object of the parent form builder. In all other cases fields_for does not use a loop.

EmFi
+1  A: 

First of all, if you need to do nested models I recommend you to view these three railscasts innmediately.

In your case I would start by interchanging the "for" and the "fields for":

<% for line_item in @order.line_items do %>
<% f.fields_for :line_item do |f| %>

... (snip)

<% end %>
<% end %>

Then I would realize that a I could move the entire fields_for to a partial view and use a render call with a :collection parameter:

in order.html.erb:
<%= render :partial => :line_item, :collection => order.line_items %>

in _line_item.html.erb:
<% f.fields_for :line_item do |f| %>
... etc

Now you don't have any "for". :)

Also, your "line items" are inside an "@order" object, so I imagine there's a form_for somewhere up:

<% form_for @order ... %>
...
  <%= render :partial => :line_item, :collection => order.line_items %>
...
<% end %>

Now you have your views fixed. But you still have to make your Orders model "handle" its children correctly.

egarcia