I'm having a bit of trouble getting forms for a has_many association to work for a shopping basket. The view is displaying the basket and has a table row for each item. Each row contains a text field so that the user can set the quantity.
The problem is that only the first item row quantity is being passed through in params, even if the second item's quantity has changed too.
Can anyone help please?
Thanks,
Roger
The output of params in the debugger is below, only one line_item is being passed through.
(rdb:2624) e params
{"commit"=>"Recalculate",
"authenticity_token"=>"7TKnhmbBPFiKLzVqTipzH8PDyCrOnKiFixGQ37XDGNY=",
"_method"=>"put", "utf8"=>"✓", "action"=>"update", "id"=>"4",
"line_item"=>{"quantity"=>"3", "id"=>"6"}, "controller"=>"baskets"}
app/controllers/basket_controller.rb
class BasketsController < ApplicationController
def update
begin
#basket = Basket.find(params[:id])
# Can't do anything here yet since the correct parameters aren't being passed through.
rescue ActiveRecord::RecordNotFound
logger.error "..."
end
redirect_to basket_path
end
end
app/views/baskets/show.html.erb
<%= form_for #basket do |f| %>
<table id="items">
<thead>
<tr>
<th>Name</th>
<th>Quantity</th>
<th>Price</th>
<th></th>
</tr>
</thead>
<tbody>
<% #basket.line_items.each do |item| %>
<%= form_for item do |g| %>
<tr class="<%= cycle('alternate', '') %>">
<td><%= item.product.name %></td>
<td>
<span class="decrement-quantity"><b>-</b></span>
<%= g.text_field :quantity %>
<span class="increment-quantity"><b>+</b></span>
</td>
<td class="price"><%= number_to_currency(item.total_price) %></td>
</tr>
<% end %>
<% end %>
<tr class="totals">
<td>Total</td>
<td><%= #basket.total_quantity %></td>
<td class="price"><%= number_to_currency(#basket.total_price) %></td>
<td></td>
</tr>
</tbody>
</table>
<%= f.submit 'Recalculate' %>
<% end %>
You're just creating a new form within the other form. Rails doesn't do anything magical just because you nest one form within another - which is what's causing the issue you're seeing.
The way to handle this situation is to use the fields_for helper and nested_attributes_for - see NestedAttributes for more information too.
I would checkout Railscasts: Complex Forms Part 1. After you watch that, you may be interested in watching Parts 2 & 3.
Ryan Bates covers using fields_for in an understandable and easy to learn fashion.
Related
Rails newbie.. I'm getting an error on my join table index page.. but only sometimes. I'm confused since sometimes it works and sometimes it doesn't. Also when I go into pry and search for what is throwing the error, the item is there.
join table relationship:
post has many calendars/calendars has many posts
calendars_posts belongs_to calendars
calendar_posts belongs_to posts
calendar_posts controller:
def index
#calendars = current_user.calendars
end
index.html.erb:
<h1>Scheduled Posts</h1>
<% current_user.calendars.each do |calendar| %>
<h2> <%= calendar.name %> </h2>
<table>
<thead>
<tr>
<th>Title</th>
<th>Date</th>
<th>Time</th>
<th>Content</th>
<th>Link</th>
<th>Picture</th>
<th>Platforms</th>
<th>Finalized</th>
</tr>
</thead>
<tbody>
<% calendar.calendar_posts.each do |calendar_post| %>
<tr>
<td><%= calendar_post.post.title.titleize %> </td>
<td> <%= calendar_post.date%> </td>
<td><%= calendar_post.time %></td>
<td> <%= calendar_post.post.content %> </td>
<td> <%= calendar_post.post.link %> </td>
<td> <%= image_tag(calendar_post.post.picture_url, width: 200) if calendar_post.post.picture.present? %> </td>
<td>
<% calendar_post.post.platforms.each do |platform| %>
<%= platform.name.titleize %> <br>
<% end %>
</td>
<td> <%= human_boolean(calendar_post.post.finalized) %> </td>
</tr>
<% end %>
</tbody>
</table>
<% end %>
I'm just confused because the error only happens sometimes... But once it starts happening then it happens every time I try to access the page after? Is there something I can do to ward off against nil?
I understand people are voting this down but I always do my research before posting and I finally figured out the issue. It was not like any of the other issues that showed up in related searches so it may be helpful for others to know... In my models I had Calendar and Posts with a join table CalendarPosts with user submittable attributes of time and date. The user can create a post and it floats separately from everything because I wanted someone to be able to jot ideas down as a "filler" and not be required to go further than that. This nil:nilclass error was coming from the fact that I had a dependency problem. My calendar_posts are dependent on posts. But I didn't have a dependency destroy in my post or calendar models. If a user deletes a post, the calendar post still existed but the post did not, which was the issue. Adding in the dependency destroy fixed my problem.
I have a form_tag with a radio_button_tag and it populates with data from DB. It must simply be directed to a customised update action(update_multiple) where a boolean column is updated for all those 2 records which have been changed in the form.
For e.g. say when initially the form was populated from DB with record 1's radio button selected and now user changed his selection to record 3, then at Submit of form tag the update of both records must occur but the problem is the code at submit, only collects id of record which is now selected in that group.How do I get id of that record also which was unselected so that I can update_all for both of them at one go?
And if Submit cannot handle this action, then is there a way in controller or form to persist the id of the initial selected record before populating the form? As you see, I've tried with collecting an array of ids[] with radio_button_tag.
TIA for your help.
Here's the form code:
<%= form_tag update_multiple_user_cv_attachments_path, method: :put, action: :update_multiple do %>
<table class="table table-bordered table-striped">
<thead>
<tr>
<th> Select a CV </th>
<th> Resume Name </th>
</tr>
</thead>
<tbody>
<% #cv_attachments.each do |cv_attachment| %>
<%= hidden_field_tag cv_attachment.main, :value => params[:main] %>
<tr>
<td><%= radio_button_tag "cv_attachment_ids[]", cv_attachment.id, cv_attachment.main %> </td>
<td><%= cv_attachment.attachment.file.basename %></td>
</tr>
<% end %>
</tbody>
</table>
<br>
<%= submit_tag "Select Main", :class =>'button' %>
<% end %>
Here's the controller update_multiple code.
def update_multiple
CvAttachment.update_all(["updated_at=?", Time.now], :id => params[:cv_attachment_ids])
end
I can think of 2 ways to achieve your objective.
update the boolean for all the attachments which belong to the user to false and then update the one which has been selected to true
include a hidden field in the form and set it to the id that is already true. Then in the controller action, update the one that is selected to true and the one in the hidden field to false. This is probably a better option and you'll probably want to wrap the d/b updates in a transaction.
<tbody>
<% #cv_attachments.each do |cv_attachment| %>
<% if cv_attachment.main %>
<%= hidden_field_tag "ex_main_cv", cv_attachment.id %>
<% end %>
<tr>
<td><%= radio_button_tag "main_cv", cv_attachment.id, cv_attachment.main %> </td>
<td><%= cv_attachment.attachment.file.basename %></td>
</tr>
<% end %>
</tbody>
controller
def update_main_attachment // probably a better name for this method
if params["ex_main_cv"] != params["main_cv"]
Attachment.transaction do
deselected_attachment = Attachment.find(params["ex_main_cv"]
deselected_attachment.update_attribute(:main, false)
selected_attachment = Attachment.find(params["main_cv"]
selected_attachment.update_attribute(:main, true)
end
end
end
Many thanks #margo. Here' how I resolved it partly your way of using hidden_field. But for now keeping this thread open as I'm making 2 DB updates for toggle of same column.
<tbody>
<% #cv_attachments.each do |cv_attachment| %>
<% if cv_attachment.main %>
<%= hidden_field_tag "ex_main", cv_attachment.id %>
<% end %>
<tr>
<td><%= radio_button_tag "new_main", cv_attachment.id, cv_attachment.main, :id => "#{cv_attachment.id}"%> </td>
<td><%= cv_attachment.attachment.file.basename %></td>
</tr>
<% end %>
</tbody>
and in controller:
def update_main
if request.put?
if params["ex_main"] != params["new_main"]
CvAttachment.find(params[:ex_main]).toggle!(:main)
CvAttachment.find(params[:new_main]).toggle!(:main)
end
end
I realize the heading is a little confusing but my problem is quite simple. I hae two models in my rails 5 app. User and Expense. Each expense belongs_to a user. I have an index page where all expenses are being listed. I can list the user IDs for each expense from the expenses table but I want to instead look up the name of the user (in column username) in the users table and display it with the expense instead. The view I have written is below. But it doesn't work.
<p id="notice"><%= notice %></p>
<h1>Teamjournals</h1>
<table style="padding: 2px; width: 50%" border="2px" align="center">
<thead>
<tr>
<td align="center"><%= link_to new_expense_path, :class =>"btn btn-success btn-wide" do%>Add New Expense<% end %></td>
</tr>
<tr>
<th>User</th>
<th>Expense Date</th>
<th>Currency</th>
<th>Expense Amount</th>
<th>Description</th>
<th colspan="1"></th>
</tr>
</thead>
<tbody>
<% #expenses.each do |expense| %>
<tr>
<td><%= User.joins(:expense).where('expense.user_id = ?', #user.id) %></td>
<td><%= expense.expense_date %></td>
<td><%= expense.currency.currency %></td>
<td align="right"><%= expense.expense %></td>
<td><%= expense.description %></td>
</tr>
<% end %>
</tbody>
</table>
Ok so in your iteration over #expenses you have this line:
<%= User.joins(:expense).where('expense.user_id = ?', #user.id) %>
you can change it to this:
<% user = expense.user %>
Note that I'm using <% not <%= because I'm just trying to assign a variable, not print the output to html.
Then after defining user you can say <%= user.name %>.
You should read a bit more about active record associations, but here's a few side comments about the query you've shown
User.joins(:expense).where('expense.user_id = ?', #user.id)
In this case, you should use the method generated by belongs_to instead of writing a query. But in situations where you do want to write a custom query, you should only be using where when you want to get an array. In this case you're looking for a single record so you could use find_by. Furthermore, the joins you're doing here is unnecessary
# any of these works
user = User.where('id = ?', expense.user_id).first
user = User.where(id: expense.user_id).first
user = user.find_by(id: expense.user_id)
I'm building a small admin page for my app that will display data from 4 models in one table. The columns are: Clubs, Users, Posts, Comments.
A club has_many users, a user has_many posts and has_many comments.
So my questions is do I need to add pagination explicitly to each of my 4 models in my admin_controller? The way it is now, I get the page list on the top and bottom of my table, and I can go back and forward pages, but all of my results are shown on the first page (~9000 results).
In my admin_controller I have
#clubs = Club.all.paginate(page: params[:page], per_page: 50)
and in my view
<%= will_paginate #clubs %>
<table>
<% i = 0 %>
<tr class="new-admin-top-row">
<td><%= "Club Location" %></td>
<td>| <%= "Number of Signups "%> </td>
<td>| <%= "Number of Posts By Users"%> </td>
<td>| <%="Number of Comments By Users"%> </td>
</tr>
<%= #clubs.find_each do |club| %>
<tr class="new-admin-row">
<td class="new-admin-cell"><%= club.name %></td>
<td class="new-admin-cell f"><%= #users_array[i] %></td>
<td class="new-admin-cell s"><%= #posts_array[i] %></td>
<td class="new-admin-cell"><%= #comments_array[i] %></td>
<td class="new-admin-cell"><%= #elevates_array[i] %></td>
<% i+=1 %>
</tr>
<% end %>
</table>
<%= will_paginate #clubs %>
The find_each method works on ActiveRecord::Relation objects and fetches 1000 records in batches. So that is where you problem most likely is. Change it to each and it'll probably solve your issue.
You can read more about find_each here: http://apidock.com/rails/ActiveRecord/Batches/find_each
I have a loop in one of my views to display a table like so:
Each category object has 5 attributes called: level_1, level_2, level_3, level_4, level_5.
There will be an array with all the category objects. so there could be any amount of categories and no necessarily 3.
what would the best way to draw this up be? i have something like this at the moment but dont know how to select the relevant category level attribute in the 5.times loop.
<table border="0">
<tr>
<th>Maturity</th>
<% for category in #categories %>
<th><%= category.category_title %></th>
<% end %>
</tr>
<% 5.times do |i|%>
<% level = 5 - i %>
<tr>
<td>Level <%= level %> Maturity</td>
<% for category in #categories %>
<td><%= category.level_ #put in the level value here so it selects the relevant attraibute %></td>
<% end %>
</tr>
<% end %>
</table>
you need to change the rendering of level with this:
<% for category in #categories %>
<td><%= category.send("level_#{level}") %></td>
<% end %>
send is used to call a method on an object so that you can compose your method at runtime.
If you categories as variable no. then you shouldn't make it columns, but rows. And then levels will be you columns e.g.
<table>
<tr>
<th>Level 1</th>
<th>Level 2</th>
<th>Level 3</th>
<th>Level 4</th>
<th>Level 5</th>
<tr>
<% #category.each do |c| %>
<tr>
<td>#category.level_1<td>
<td>#category.level_2<td>
<td>#category.level_3<td>
<td>#category.level_4<td>
<td>#category.level_5<td>
<th>
<% end %>
Now in above code you may replace hard coded level_#{no} with iterations.