Newbie stuck with fields_for and nested attributes in rails - ruby-on-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 %>

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.

Ruby on Rails - How to recall a method from a closed do block

I've got this code in my matches index:
<table>
<% #matches.each do |match| %>
<tr>
<td><%= match.team_a %></td>
<td><%= match.team_b %></td>
<td><%= match.score_a %> - <%= match.score_b %></td>
<td><%= match.field %></td>
<%= render "shared/asterisk_message", :target => [match.team_b, match.team_a] %>
<% end %>
</table>
I want to move the rendered asterisk_message outside the table, but this would mean to put it after the <% end %>, which obviously gives me this error:
undefined local variable or method `match' for #<#:0xb6da40d8>
How can I work it out?
Try:
<table>
<% #matches.each do |match| %>
<tr>
<td><%= match.team_a %></td>
<td><%= match.team_b %></td>
<td><%= match.score_a %> - <%= match.score_b %></td>
<td><%= match.field %></td>
<%= render "shared/asterisk_message", :target => [match.team_b, match.team_a] %>
<% end %>
</table>
<% #matches.each do |match| %>
<%= render "shared/asterisk_message", :target => [match.team_b, match.team_a] %>
<% end %>

Edit Multiple (reference Railscasts Episode #165)

I'm following Railscasts Episode #165 Edit Multiple but having issue where when I go to the product index page it is not showing my list of products. I'm getting only the header and the link to new product. Any idea what I did wrong?
By the way I'm using rails 3.2.3
Thank you.
routes.rb
resources :products do
collection do
post :edit_multiple
put :update_multiple
end
end
resources :categories
index.html.erb
<h1>Listing products</h1>
<% form_tag edit_multiple_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><%= product.name %></td>
<td><%= product.category.name %></td>
<td><%= 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>
<%= submit_tag "Edit Checked" %>
<% end %>
<p><%= link_to "New Product", new_product_path %></p>
You need <%= form_tag ... instead of <% form_tag .... Without the = the return value (i.e. your form) is discarded rather than added to the output.
I think this is typo.
you forgot to use "=" You should do <%= form_tag edit_multiple_products_path do %>

how to display errors notice in Rails?

Hi I have the following code in my model:
class Product < ActiveRecord::Base
before_destroy :check_tasks
has_many :tasks, :order => 'created_at DESC'
validates :name, :presence => true
belongs_to :sprint
validates :sprint_id, :presence => true
def check_tasks
if self.tasks.any?
errors.add_to_base "Product has tasks and cannot be destroyed."
return false
end
end
end
When I am in the product view which lists all the products, I would like to display the error message on the top of the view, every time someone tries to delete a product which has tasks linked to it. I want the message: Product has tasks and cannot be destroyed displayed.
What code should I put in the view below? The view is the product view, which is in the views/products folder.
Thanks!!!
<h1>Listing products</h1>
<%= link_to 'New Product', new_product_path %>
<table>
<tr>
<th>Name</th>
<th>Description</th>
<th>Sprint</th>
<th></th>
<th></th>
<th></th>
</tr>
<% if not #messages_rendered -%>
<% if flash[:error] -%>
<p class='error'><%=h flash[:error] %></p>
<% end -%>
<% if flash[:notice] -%>
<p class='notice'><%=h flash[:notice] %></p>
<% end -%>
<% end -%>
<% #messages_rendered = true -%>
<% #products.each do |product| %>
<tr>
<td><%= product.name %></td>
<td><%= product.description %></td>
<td><%= product.sprint.name %></td>
<td><%= link_to 'Show', product %></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>
<br />
Try putting this in your product_controller:
def destroy
...
flash[:error] = #product.errors.full_messages.join(' ')
...
end

How do I do this without using a double loop?

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

Resources