How to dynamically generate checkboxes on the view and submit to controller? - ruby-on-rails

I send a collection to my view and iterate over it. Each iteration has a checkbox as well as two text fields. I want to be able to submit selected items, but also submit the values in the text field as an update as well.
<% #lots.each_with_index do |lot, index| %>
<tr>
<td><%= lot.lot_code %></td>
<td ><%= check_box_tag "lots[][lot_id]", lot.id, false, %></td>
<td><%= text_field_tag "lots[][seal_num]", lot.seal_number %></td>
<td><%= text_field_tag "lots[][contr_num]", lot.container_number %></td>
</tr>
<% end %>
My #lots collection has three rows:
{id: 2, container_number: "container#0002", seal_number: "seal#0002"}
{id: 6, container_number: "container#0006", seal_number: "seal#0006"}
{id: 11, container_number: "container#00011", seal_number: "seal#00011"}
(apologies if that is not proper hash syntax)
My problem is that if I check items 1 and 3, the parameter string shows the following:
"lots"=>[{"lot_id"=>"2", "seal_num"=>"seal 0002", "contr_num"=>"container#0002"},
{"seal_num"=>"Seal-006", "contr_num"=>"container #006", "lot_id"=>"11"},
{"seal_num"=>"Seal-0011", "contr_num"=>"container #0011"}]
The problem is that lot_id = 11 should be the 3rd item in the array, not the second. For some reason, because item #2 is not selected, the empty lot_id element is being populated by the next one in the sequence.
How do I make sure the selected IDs are in the same elements as the other attributes? Or how do I make sure the non-selected items don't even end up on the parameter string?
Edit: I have also tried the following:
<% #lots.each_with_index do |lot, index| %>
<tr>
<td><%= lot.lot_code %></td>
<td ><%= check_box_tag "lots[#{index}][lot_id]", lot.id, false, %></td>
<td><%= text_field_tag "lots[#{index}][seal_num]", lot.seal_number %></td>
<td><%= text_field_tag "lots[#{index}][contr_num]", lot.container_number %></td>
</tr>
<% end %>
And I get this on the following parameter string:
lots"=>{"0"=>{"lot_id"=>"2", "seal_num"=>"seal 0002", "contr_num"=>"container#0002"},
"1"=>{"seal_num"=>"Seal-006", "contr_num"=>"container #006"},
"2"=>{"lot_id"=>"19", "seal_num"=>"Seal-0011", "contr_num"=>"container #0011"}}
This maintains the integrity of the rows as desired, but I later get a "TypeError: no implicit conversion of String into Integer" later on in processing.
Edit 2: Realized the solution in the edit created a hash, and my code was looking to access an array. So when the hash was iterated over, I was errantly accessing the key, an integer, rather than the value.

Related

Correct way of conditional rendering within a partial in Rails?

I have created a partial for a LineItem that, in my case, emits a <tr> with a couple of <td>s. This works just fine and dandy.
What I'd want to do then, is use the same partial in different places. The original view has plenty of room, so displaying every cell is ok. But another (a sidebar), has much less space, and I'd only want to display two of the 5 cells.
So the question, then, is what is the correct way of implementing this? I'd like for the "full" version to be the default unless otherwise specified, and only use the limited version if a parameter is passed.
Currently I'd probably use the feature of passing locals to a partial and check if a parameter limited is defined, and if so, skip the last N cells. Am I on a right track, and if so, what kind of a variable should I use (can I use symbols for this)?
Current _line_item.html.erb (that I'm somewhat unhappy with)
<tr>
<td><%= line_item.quantity %>×</td>
<td><%= line_item.product.title %></td>
<td class="item_price"><%= number_to_currency(line_item.total_price, unit: '€') %></td>
<% unless defined? limited %>
<td class="remove_item"><%= button_to 'Remove', line_item, method: :delete %></td>
<% end %>
</tr>
Edit: Added current version.
Yes, you are on the right track. You can do something like
<%= render 'partial_name', limited: true %>
and then in the partial:
<% limited ||= false %> # More explicit as to what default value is
<tr>
<td><%= line_item.quantity %>×</td>
<td><%= line_item.product.title %></td>
<td class="item_price"><%= number_to_currency(line_item.total_price, unit: '€') %></td>
<% unless limited %>
<td class="remove_item"><%= button_to 'Remove', line_item, method: :delete %></td>
<% end %>
</tr>

How to use form input(s) in calculations?

I am putting together a survey-submitting website, and I am giving visitors the option of specifying the time and date of a particular event they created:
<table>
<tr>
<td><%= text_field(:day, "", placeholder: "DD") %></td>
<td><%= text_field(:month, "", placeholder: "MM") %></td>
<td><%= text_field(:year, "", placeholder: "YYYY") %></td>
<td><%= text_field(:hour, "", placeholder: "HH") %> : <%= text_field(:minute, "", placeholder: "MM") %> </td>
<td><%= radio_button_tag(:ampm, "AM") %></td><td><%= label_tag(:ampm, "AM") %></td>
<td></td>
<td><%= radio_button_tag(:ampm, "PM") %></td><td><%= label_tag(:ampm, "PM") %></td>
</tr>
</table>
My Survey model has a :time_of_event attribute that holds a Time object which will represent the time specified by the user that fills out the form. I intend to make use of Time.parse to create a Time object out of the 'YYYY-MM-DD HH:MM:00' that the user specifies.
I have minimal experience using Rails forms - I can create basic text_field tags, and can save the input in those tags to a particular model's attribute upon submitting the form.
How (if at all) can I go about calculating the input in form fields, and save that calculated result to a model's attribute?
In other words, something that will probably end up looking like:
survey.time_of_event = Time.parse(form_params[:year] + '-' + form_params[:month] ...etc)

How to sum object column?

I need to make sum of column.
I think problem is in class, in action where I take performance_reports.
I have associsations, so I get all reports related to user by next code:
#performance_reports = Array.new
current_user.websites.each do |website|
reports = website.performance_reports
reports.each do |report|
#performance_reports.push(report)
end
end
In my view I'm printig each report and in the bottom I want to print summary of column.
Here is view:
<% #performance_reports.each do |performance_report| %>
<tr>
<td><%= performance_report.impressions %></td>
<td><%= performance_report.clicks %></td>
<td><%= performance_report.conversions %></td>
</tr>
<% end %>
<tr>
<td><%= #performance_reports.count(:clicks)%></td>
<td><%= #performance_reports.count(:conversions)%></td>
</tr>
All - clicls, conversions - are integer type.
This code is printing, that summary is 0.
I have two questions: how to sum integers and how to sum not integer columns, which are not saving to database?
you can use the method sum for this
#performance_reports.sum(:clicks)
it will also sum not integer also as it will return non integer type, to convert it in integer call like this
#performance_reports.sum(:clicks).to_i

Rails - add a dropdown selection to index with db update on change

I've added dropdown selections to forms before, but am having trouble getting them to work in the index view. I've read the API on this and can't get anything to work.
Basically, I've got a list of all tasks in the index view. I set the default in the db to 'No' in a migration. I want to be able to loop through all the tasks and have a dropdown selection appear with the options no, yes and partly. Then I want to be able to have the db update that task's field with the selection when it is made.
I can't get the dropdown to work - whenever I try using task.select, it says that select is a private method. I apologise, I'm fairly new to rails - any help anyone could give would be fantastic! How do I get the dropdown to work and how do I ensure that the db gets updated when the value is changed?
Thanks!
View code (here's what I have so far) - tasks/index:
<table class="table table-striped table-bordered">
<tr>
<th>Name</th>
<th>Category</th>
<th>Notes</th>
<th>Completed?</th>
<th></th>
<th></th>
<th></th>
</tr>
<% #tasks.each do |task| %>
<tr>
<td><%= task.name %></td>
<td><%= task.category %></td>
<td><%= task.notes %></td>
<td><%= task.select :complete, ['No','Yes','Partly'], :selected => 'No' %></td>
<td><%= link_to 'Show', task %></td>
<td><%= link_to 'Edit', edit_task_path(task) %></td>
<td><%= link_to 'Delete', task, confirm: 'Are you sure?', method: :delete %></td>
</tr>
<% end %>
</table>
UPDATE:
After more research, I've changed the line of code:
<td><%= task.select :complete, ['No','Yes','Partly'], :selected => 'No' %></td>
to:
<% form_for task do |f| %>
<td><%= select :task, :complete, [ ["Yes",2], ["Partly",1], ["No",0]] %></td>
<% end %>
This has stopped throwing up errors when I try and load the page, however, there is nothing where the 'form' should be - and nothing in the source code, these lines are skipped. How can I get this form to appear and how can I get it to change the database when the selection is changed? Thanks!
Suppose that Post::CATEGORIES = ["No", "Yes", "Partly"]
It depends on how you store task.category in your DB.
If it is an integer in (0, 1, 2)
form_for task do |f|
f.select :category, Post::CATEGORIES.map { |c| [c, Post::CATEGORIES.index(c)] }, :selected => task.category, :include_blank => true
end
If it is a string in ("No", "Yes", "Partly")
form_for task do |f|
f.select :category, Post::CATEGORIES, :selected => task.category, :include_blank => true
end
To be able to make a change to your models, you'll of course need to submit the form somehow. You could use one form for all the records with:
One submit button: you'll need to change the form and your update controller to receive multiple tasks
A form per task with multiple submit buttons: your user will need to submit each time he changes the category
Submit using AJAX each time the user changes the category.
Good luck
I think what you're looking for is a form_for and then a f.select. You need to create the form first and then f.select is the drop down. Something like this:
form_for task do |f|
f.select("task", "category", Post::CATEGORIES, {:include_blank => true})
end
replace Post::CATEGORIES with the list of options for the dropdown

fields_for in table produces technically incorrect HTML

When I use a construct like:
<table>
<%= f.fields_for :group_locations do |gl| %>
<tr>
<td><%= gl.label :group_id %></td>
<td><%= gl.select :group_id, paths %></td>
</tr>
<% end %>
</table>
I get an error in my browser (Safari) for each row in the table (<input> is not allowed inside <tbody>. Inserting <input> before the <table> instead.) This is caused by the hidden <input> for the association's id being placed after the </tr>. How can I cause the id's <input> to appear inside one of the TD elements?
I think the hidden field won't be printed if you print it manually. Could you try this?
<table>
<tr>
<%= f.fields_for :group_locations do |gl| %>
<td><%= gl.hidden_field :id %></td>
<td><%= gl.label :group_id %></td>
<td><%= gl.select :group_id, paths %></td>
<% end %>
</tr>
</table>
The fields_for method concatenates the hidden id <input> to the end of the block it captures. So, if you put your <% end %> tag before your second </td>, you should get the result you want.
This is more of a comment (but I don't seem to be able to comment directly). Just wanted to note that Dogbert's answer worked for me. The malformed html didn't seem to worry most browsers... until I tried to use jquery ui sortable on the the table in IE8, which caused a number of problems (including a crash). Anyway, explicitly including the id in a hidden field inside a td seems the way to go. You may want to consider only doing this if the object is persisted (otherwise the hidden field has no value, which may or may not be an issue depending on your code). Adding a check to see if an object is persisted might look like (in the above case):
<% if gl.object.persisted? %>
<td><%= gl.hidden_field :id %></td>
<% end %>

Resources