How do I do this without using a double loop? - ruby-on-rails

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 %>

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 %>
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.

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.

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".

Related

Rails: Complete object displayed instead of parameter

I have a web app that displays questions with options. The problem is for the options it is displaying whole object along with the body parameter of the object. I do not know what is happening here. Is it something related to serializing and deserializing?
index.html.erb
<tbody>
<% #questions.each do |question| %>
<tr>
<td><%= question.body %></td>
<td><%= question.user.email %></td>
<td>
<%= question.options.each do |p| %>
<%= radio_button_tag('option',p.id) %>
<%= p.body %>
<% end %>
</td>
<td><%= link_to 'Show', question %></td>
<% if current_user && current_user.admin %>
<td><%= link_to 'Edit', edit_question_path(question) %></td>
<td><%= link_to 'Destroy', question, method: :delete, data: { confirm: 'Are you sure?' } %></td>
<% end %>
</tr>
<tr>
</tr>
<% end %>
</tbody>
controller_params
private
def set_question
#question = Question.find(params[:id])
end
def question_params
params.require(:question).permit(:body, options_attributes: [:body])
end
model
class Question < ApplicationRecord
belongs_to :user
has_many :options, dependent: :delete_all, :autosave => true
validates_length_of :options, maximum: 4
accepts_nested_attributes_for :options
end
output
<%= question.options.each do |p| %>
<%= radio_button_tag('option',p.id) %>
<%= p.body %>
<% end %>
Your problem is with this block. Your first part of ERB is telling the view to render question.options.each as well as the followowing HTML, resulting in the objects themselves being rendered. Changing to:
<% question.options.each do |p| %>
<%= radio_button_tag('option',p.id) %>
<%= p.body %>
<% end %>
Will fix your issue.

Newbie stuck with fields_for and nested attributes in rails

Rails newbie here. I'm stuck on fields_for in Rails, and would be eternally grateful for anyone's help.
In Events#new, why am I getting NoMethodError?
Error message says undefined method `name'
In Events, why am I getting NameError?
Error message says undefined local variable or method 'event'.
I am trying to create and view a form where each event has 2 fields for each friend.
_form.html.erb:
<%= form_for(#event) do |f| %>
<div class="field">
Date: <%= f.text_field :date %></br>
Friends: <%= f.fields_for :friends do |friends_fields| %>
<%= friends_fields.text_field :name %>
<% end %>
<% end %>
index.html.erb:
<tbody>
<% #events.each do |event_form| %>
<tr>
<td><%= event_form.date %></td>
<td><%= event_form.friends.each do |friend| %>
<%= friend.name %>
<% end %>
</td>
<td><%= link_to 'Show', event %></td>
<td><%= link_to 'Edit', edit_event_path(event) %></td>
<td><%= link_to 'Destroy', event, method: :delete, data: { confirm: 'Are you sure?' } %></td>
</tr>
<% end %>
event.rb:
class Event < ActiveRecord::Base
has_many :friends
accepts_nested_attributes_for :friends, allow_destroy: true
end
friend.rb:
class Friend < ActiveRecord::Base
belongs_to :event
end
events_controller.rb:
def new
#event = Event.new
2.times { #event.friends.build }
end
Event is not defined because events iterator uses event_form instead of event variable name in each iteration. Try changing to this:
<tbody>
<% #events.each do |event| %>
<tr>
<td><%= event.date %></td>
<td><%= event.friends.each do |friend| %>
<%= friend.name %>
<% end %>
</td>
<td><%= link_to 'Show', event %></td>
<td><%= link_to 'Edit', edit_event_path(event) %></td>
<td><%= link_to 'Destroy', event, method: :delete, data: { confirm: 'Are you sure?' } %></td>
</tr>
<% end %>

Rails - update multiple resource entries with one link

I have a table of forms
<% #group.lessons.each do |lesson| %>
<%= form_for [#group, lesson] do |f| %>
<tr id='<%= lesson.id%>' >
<td><%= f.text_field :time %></td>
<td><%= f.text_field :day %></td>
<td><%= f.text_field :subject %></td>
<td><%= f.text_field :teacher %></td>
<td><%= f.text_field :room %></td>
<td><%= f.submit 'Update'%></td>
<td><%= link_to 'Delete', [lesson.group, lesson], method: :delete%></td>
</tr>
<%end%>
<%end%>
Each form updates an entry when the "update" button is clicked. But when you edit two entries and update only one, the info you edited in the other one is gone.
I want to have a button to update each entry in the table. How do I do this?
UPDATED:
First in model:
class Group < AR::Base # possibly you'r using ActiveRecord
attr_accessible :lessons_attributes
accepts_nested_attributes_for :lessons, :allow_destroy => true
has_many :lessons
end
and then in your view: # e.g. views/groups/_form.html.erb
<table>
<%= form_for #group do |f| %>
<%= f.error_messages %>
<%= f.fields_for :lessons do |lesson_form| %>
<%= render "lessons/lesson", :f => lesson_form%>
<% end %>
<tr><td><%= f.submit 'Update'%></td></tr>
<% end %>
</table>
and in views/lessons/_lesson.html.erb
<tr>
<td>
<%= f.text_field :subject %>
<%= f.check_box :_destroy %>
<%= f.label :_destroy, "Remove Lesson" %>
</td>
</tr>
I think this is more of an html issue than a rails issue. You simply can't submit more than one form using plain html. That's why you're losing one form when you submit the other.
What you could do however is update multiple lessons that belong to the same group using nested attributes. Here are some resources for that:
http://api.rubyonrails.org/classes/ActiveRecord/NestedAttributes/ClassMethods.html
http://railscasts.com/episodes/196-nested-model-form-part-1
https://github.com/ryanb/nested_form

Railscast 198, but using formtastic

How could you do what's covered in RyanB's Railscast on editing multiple records individually, using Formtastic? Formtastic doesn't use form_tag, which RyanB's method relies on.
The semantic_form_for is just a wrapper around form_for so you can use the same parameters. Here is a formtastic version of Ryan Bates' screencast
views/products/edit_individual.html.erb
<% semantic_form_for :update_individual_products, :url => update_individual_products_path, :method => :put do |f| %>
<% for product in #products %>
<% f.fields_for "products[]", product do |ff| %>
<h2><%=h product.name %></h2>
<%= render "fields", :f => ff %>
<% end %>
<% end %>
<p><%= submit_tag "Submit" %></p>
<% end %>
views/products/index.html.erb
<% semantic_form_for :edit_individual_products, :url => edit_individual_products_path do %>
<table>
<tr>
<th></th>
<th>Name</th>
<th>Category</th>
<th>Price</th>
</tr>
<% for product in #products %>
<tr>
<td><%= check_box_tag "product_ids[]", product.id %></td>
<td><%=h product.name %></td>
<td><%=h product.category.name %></td>
<td><%= number_to_currency product.price %></td>
<td><%= link_to "Edit", edit_product_path(product) %></td>
<td><%= link_to "Destroy", product, :confirm => 'Are you sure?', :method => :delete %></td>
</tr>
<% end %>
</table>
<p>
<%= select_tag :field, options_for_select([["All Fields", ""], ["Name", "name"], ["Price", "price"], ["Category", "category_id"], ["Discontinued", "discontinued"]]) %>
<%= submit_tag "Edit Checked" %>
</p>
<% end %>
Please note that you can use the form_for helpers as well in formtastic.
Update
If you like to use nested attributes as well it should work out of the box, using fields_for on the form partial. Lets stick with the railscast example and say that:
product.rb
has_many :commments
accepts_nested_attributes_for :comments
You can edit the comments on the _fields.html.erb of the products like:
<%= f.fields_for :comments do |cf| %>
<%=render 'comments/fields', :f=>cf%>
<%end%>
And make sure you have a fields partial in your comments views.

RoR: fields_for and partials

i am using nested_attributes for an association of products back to a transaction.
I need to add a date to the products model. I would like to add a link to set the date_select to today.
here is the view:
<% f.fields_for :trans_products do |builder| %>
<%= render 'trans_product_fields', :f => builder %>
<% end %>
and the partial:
<tr class="fields">
<td><%= f.text_field :po, :class => 'prodinfo' %></td>
<td><%= f.text_field :so, :class => 'prodinfo' %></td>
<td><%= f.text_field :product, :class => 'prodinfo' %></td>
<td><%= f.text_field :serial, :class => 'prodinfo' %></td>
<td class=bigprodinfo><%= f.date_select :pos_date, :include_blank => true, :order => [:month, :day, :year] %>
<%= link_to_function("today", "set_today('#{????}','pos_date')", :class => "today_link") %> </td>
<td><%= link_to_remove_product "remove", f %></td>
</tr>
the ???? should read transaction_trans_products_attributes_#number#, #number# shows up correctly for the textfields, the po text field reads id="transaction_trans_products_attributes_0_po" the next trans_product has id="transaction_trans_products_attributes_1_po"
how can i get this counter value into my link_to_function parameter?
thx
There should be an iteration_counter automatically set up for your collection (your collection here being called "trans_products".
According to the docs, the counter is named after the items in the array eg here it should be called "trans_product_counter"
For more info, have a look here:
http://apidock.com/rails/ActionView/Partials

Resources