I have a table that has X rows. I use an iterator to iterate over each object in a collection and render it in a row.
Each object/row is also associated with it's own form.
But I want to save the entire collection at once, and have it update all of the sub-forms when the user hits 1 submit button, rather than 1 for each instance of the form.
This is an example of what the code might look like:
<% #port_stocks.each do |port_stock| %>
<tr>
<td><%= number_to_currency(port_stock.purchase_price) %></td>
<td><%= port_stock.volume %></td>
<td>
<% cp = ClosedPosition.new %>
<%= simple_form_for cp, url: port_stocks_sell_order_path, method: :post, html: { class: "form-inline" } do |f| %>
<div class="form-group">
<%= f.input_field :num_units, id: "sell-ps-#{port_stock.id}", class: "form-control mx-sm-3" %>
</div>
<%= f.submit "Save", class: "btn btn-primary" %>
<% end %>
</td>
</tr>
<% end %>
If there are 3 port_stock objects in the #port_stocks collection, the above form renders 3 submit buttons -- which is exactly what I don't want.
I just want 1 submit button that submits a cp related info for each port_stock object in the collection.
You could hide the submit button inside the simple_form like below:
<% #port_stocks.each do |port_stock| %>
<tr>
<td><%= number_to_currency(port_stock.purchase_price) %></td>
<td><%= port_stock.volume %></td>
<td>
<% cp = ClosedPosition.new %>
<%= simple_form_for cp, url: port_stocks_sell_order_path, method: :post, html: {class: "form-inline"} do |f| %>
<div class="form-group">
<%= f.input_field :num_units, id: "sell-ps-#{port_stock.id}", class: "form-control mx-sm-3" %>
</div>
<%= f.submit "", class: "click-me", style: "display: none;" %>
<% end %>
</td>
</tr>
<% end %>
Then have a button outside the loop like this:
<button type="button" class='btn btn-primary' onclick="submit()">Submit!</button>
Onclick of this button use JS to trigger clicks on hidden submit buttons.
<script>
function submit(){
var el = document.getElementsByClassName('click-me');
for (var i=0;i<el.length; i++) {
el[i].click();
}
}
</script>
Just an idea, following up my comment, using standard form helpers.
Wrap the table with the form:
<%= form_for :closed_position, url: {action: :port_stocks_sell_order_path, method: :post} do |form|%>
<table>
<% #port_stocks.each do |port_stock| %>
<tbody>
<tr>
<td><%= port_stock.volume %></td>
<td><%= port_stock.purchase_price %></td>
<td>
Num: <%= form.text_field "port_stock_id[#{port_stock.id}][num_units]" %>
</td>
</tr>
</tbody>
<% end %>
</table>
<%= form.submit "save" %>
<% end %>
The parameters should be something like:
{"utf8"=>"✓", "authenticity_token"=>"blahblah", "closed_position"=>{"port_stock_id"=>{"1"=>{"num_units"=>"999"}, "2"=>{"num_units"=>"888"}}}, "commit"=>"save", "method"=>"post", "controller"=>"port_stocks", "action"=>"port_stocks_sell_order_path"}
Then manage to save each ClosedPosition in the controller.
Related
I have a form that users enter data in, with a button to save the data. The form also has buttons to send the user to other forms to enter more info.
<%= form_with(model: [order_header], local: true) do |f| %>
<%= render "shared/errors", object: order_header %>
<%= f.hidden_field(:customer_id, value: #customer.id) %>
<%= f.label :status %>
<%= f.select(:status, [['Quote', 1], ['Order', 3], ['Invoice', 5]]) %>
<%= f.label :customer_purchase_order %>
<%= f.text_field :customer_po %>
<h4>Lines</h4>
<table cellpadding=10>
<% #orderlines = order_header.order_lines %>
<% #orderlines.each do |line| %>
<tr>
<td><%= line.line_type %></td>
<td><%= line.description %></td>
<td><%= link_to 'Edit', edit_design_item_path(line.design_item_number), class: "inline_button" %></td>
<% end %>
</tr>
</table>
<section class="button-bar">
<%= f.submit "Save", class: "button" %>
</section>
<% end %>
If the user hits an 'Edit' button, before hitting the 'Save' button, the data entered on the main form is lost. Is there a way to automatically save the data on the main form before calling the other form via the edit button?
I have a situation where I have to nest form tag inside form because of HTML flow but it can't be done. Are there any alternatives for this?
<tr class="first last">
<td class="a-right last" colspan="50"><button onclick="setLocation('#')" class="button btn-continue" title="Continue Shopping" type="button"><span>Continue Shopping</span></button>
<%= button_tag class: 'button btn-update', id: 'update-button' do %>
<%= Spree.t(:update) %>
<% end %>
<%= form_tag empty_cart_path, method: :put do %>
<%= submit_tag Spree.t(:empty_cart), class: 'button btn-empty' %>
<% end %>
</td>
</tr>
</tfoot>
<%= form_for #order, url: update_cart_path, html: { id: 'update-cart' } do |order_form| %>
<%= render partial: 'form', locals: { order_form: order_form } %>
<% end %>
<% end %>
button_tag should be inside form_for form, but if I set it like this:
<td class="a-right last" colspan="50"><button onclick="setLocation('#')" class="button btn-continue" title="Continue Shopping" type="button"><span>Continue Shopping</span></button>
<%= form_for #order, url: update_cart_path, html: { id: 'update-cart' } do |order_form| %>
<%= button_tag class: 'button btn-update', id: 'update-button' do %>
<%= Spree.t(:update) %>
<% end %>
<%= form_tag empty_cart_path, method: :put do %>
<%= submit_tag Spree.t(:empty_cart), class: 'button btn-empty' %>
<% end %>
</td>
</tr>
</tfoot>
<%= render partial: 'form', locals: { order_form: order_form } %>
<% end %>
<% end %>
it's not working
Can I nest form tag inside form?
You cannot nest form tags.
It is wrong. It won't work because it is wrong. Most browsers will only see one form.
https://www.w3.org/TR/html5/forms.html#the-form-element
Content model:
Flow content, but with no form element descendants.
If I'm correct you like to have that empty_cart button aligned with your update button. In Rails you can create a link that does an update or post or whatever you like. You can do something like:
<%= link_to Spree.t(:empty_cart(_method: 'put')), empty_cart_path, class: 'button btn-empty', method: :post %>
I have an index page with a partial form to submit new records to Package model. I keep this form in the index page, so the user doesn't need to leave the page when repeating this action, several times.
In the same page I have a form_tag fir multiple updates for the same controller, namely packages_controller.
Everything works fine, except the following: when hit the update button, going to the form BUT instead of submitting I go back (with the browser) and try to select other records to be updated then I have a routing error:
Routing Error
No route matches [PUT] "/projects/47/orderlines/18/packages"
My index page looks like this:
<% if current_user %>
<%= render "packages/form" %>
<% end %>
<% if #packages.count >= 1 %>
<table class="table table-striped">
<thead>
<tr>
<th> <input type="checkbox" id="selectAll" value="selectAll"></th>
<th>Packed </th>
<th>#No.</th>
<th>Type</th>
<th>Gross weight</th>
<th>Length</th>
<th>Width</th>
<th>Height</th>
<th></th>
<th>Container</th>
</tr>
</thead>
<%= form_tag edit_multiple_project_orderline_packages_path, method: :get do %>
<tbody>
<% for package in #packages %>
<% if package.packed== true %>
<% #label_type="success" %>
<% else %>
<% #label_type="default" %>
<% end %>
<tr>
<td><%= check_box_tag "package_ids[]", package.id %></td>
<td><span class="label label-<%= #label_type %>"><% if package.packed==true %>Packed<% else %>Unpacked<% end %></span></td>
<td><%= package.package_no %></td>
<td><%= package.package_type %></td>
<td><%= package.gross_weight %></td>
<td><%= package.length %></td>
<td><%= package.width %></td>
<td><%= package.height %></td>
<% if #orderline.packages.count >= 1 %>
<td><%= link_to 'Delete', [package.orderline.project, package.orderline, package],
method: :delete,
data: { confirm: 'Are you sure?' } %></td>
<td><%= #containers.find(package.container_id).container_id if package.packed %></td>
<% end %>
</tr>
<% end %>
</tbody>
</table>
<%= submit_tag "Add to container", class: "btn btn-primary" %>
<% end %>
<br />
<%= will_paginate %>
<br>
And the multiple_edit form
<div class="col-sm-4">
<%= form_tag update_multiple_project_orderline_packages_path, method: :put do %>
<ul>
<% #packages.each do |package| %>
<li>
<%= hidden_field_tag "package_ids[]", package.id %>
<%= package.package_no %>
<%= package.container_id %>
<% package.errors.full_messages.each do |msg| %>
<li><%= msg %></li>
<% end %>
</li>
<% end %>
</ul>
<%= fields_for :package do |f| %>
<div class="field">
<%= f.label :package_no %><br />
<%= f.text_field :package_no, :class => "form-control" %>
</div>
<br />
<div class="field">
<%= f.label :container_id %><br />
<%= select_tag 'package[container_id]', options_from_collection_for_select(#project.containers, 'id', 'container_id', default_blank: true), prompt: "- Select container -", :class => "form-control" %>
</div>
<br />
<div class="field">
<%= f.label :packed %><br />
<%= f.select :packed, [["Packed", true], ["Unpacked", false]],{ prompt: "- Packing -"},{ :class => "form-control" } %>
</div>
<% end %>
<div class="actions">
<br />
<%= submit_tag "Update", :class => "btn btn-primary" %>
</div>
<% end %>
</div>
And the packages controller edit_multiple actions:
def edit_multiple
#project = Project.find(params[:project_id])
#packages = Package.find(params[:package_ids])
end
def update_multiple
#packages = Package.find(params[:package_ids])
#packages.reject! do |package|
package.update_attributes(package_params.reject { |k,v| v.blank? })
end
if #packages.empty?
redirect_to project_orderline_packages_url
else
#package = Package.new(package_params)
render "edit_multiple"
end
end
packages_controller create action:
def create
project = Project.find(params[:project_id])
orderline = project.orderlines.find(params[:orderline_id])
#package = orderline.packages.new(package_params)
#package.save
if #package.save
flash[:success] = "Package(s) was successfully added."
redirect_to :back
else
render 'new'
end
And my routes:
resources :projects do
resources :containers
resources :orderlines do
resources :packages do
collection do
put :packed
get :edit_multiple
put :update_multiple
end
end
end
end
I just added my routes here:
edit_multiple_project_orderline_packages_path GET /projects/:project_id/orderlines/:orderline_id/packages/edit_multiple(.:format)
packages#edit_multiple
update_multiple_project_orderline_packages_path PUT /projects/:project_id/orderlines/:orderline_id/packages/update_multiple(.:format)
packages#update_multiple
project_orderline_packages_path GET /projects/:project_id/orderlines/:orderline_id/packages(.:format)
packages#index
POST /projects/:project_id/orderlines/:orderline_id/packages(.:format)
packages#create
new_project_orderline_package_path GET /projects/:project_id/orderlines/:orderline_id/packages/new(.:format)
packages#new
edit_project_orderline_package_path GET /projects/:project_id/orderlines/:orderline_id/packages/:id/edit(.:format)
packages#edit
project_orderline_package_path GET /projects/:project_id/orderlines/:orderline_id/packages/:id(.:format)
packages#show
PATCH /projects/:project_id/orderlines/:orderline_id/packages/:id(.:format)
packages#update
PUT /projects/:project_id/orderlines/:orderline_id/packages/:id(.:format)
packages#update
DELETE /projects/:project_id/orderlines/:orderline_id/packages/:id(.:format)
your form_tag code is update_multiple_project_orderline_packages_path
I think it should be update_multiple_project_orderline_package_path(project_id, orderline_id, package_id)
I am not 100% sure with my statement above, because you gave scrambled Rails Routes, hard to read
and your form action seems goes to packages#edit_multiple controller
so paste your edit_multiple method, not create method
are you implementing your scenario above with javascript, or just plain HTML?
I have three models PriceGroup, PriceGroupLine and Item.
PriceGroup has fields - :id and :name
Item has fields - :id and :name
PriceGroupLine has fields - :id, :price_group_id, :item_id, :price
Associations:
PriceGroup has_many PriceGroupLines
PriceGroupLine has_many Items
PriceGroupLine belongs_to PriceGroup
I need to show strings from PriceGroupLine model on show.html.erb of PriceGroup model. It looks like table (i can't post images - low reputation ((( ), ITEM - PRICE for current Price group. There is can be many PriceGroupLines in PriceGroup. They all must be shown for current Price Group.
I am new in Rails. Can you show me the way to solve my problem
UPDATE
I also need to insert in PriceGroupLine model lines from PriceGroup show view. How I should organize form_for helper if if I need to insert:
item_id - items list organized with collection_select helper
price_group_id
price of item
Here is my code of view show.html.erb of PriceGroup. Adding a new item in price group (through price group line) not working. undefined method 'price' for PriceGroup:0x007f7201dc0aa8 error
<div class="container col-md-5">
<h4>PriceGroup Info</h4>
<%= link_to "Add new price group", new_price_group_path, class: "btn btn-primary btn-xs", role: "button" %>
<%= link_to "Back", price_groups_path, class: "btn btn-primary btn-xs", role: "button" %>
<table class="table table-hover table-condensed table-bordered">
<tr>
<td><strong>Price Group name</strong></td>
<td><%= #price_group.name %></td>
</tr>
</table>
<h4>Price Group Content</h4>
<table class="table table-hover table-condensed table-bordered">
<tr>
<td><strong>Price Group Name</strong></td>
<td><strong>Price</strong></td>
<td><strong>Operations</strong></td>
</tr>
<% #price_group.price_group_lines.each do |price_group_line| %>
<tr>
<td><%= price_group_line.item.try(:name) %></td>
<td><%= price_group_line.price %></td>
<td><%= link_to "Edit", edit_price_group_line_path(price_group_line), class: "btn btn-primary btn-xs", role: "button" %> | <%= link_to "Delete", price_group_line_path(price_group_line), method: :delete, data: { confirm: "Sure?" }, class: "btn btn-primary btn-xs", role: "button" %></td>
</tr>
<% end %>
</table>
</div>
<div class="container col-md-10">
<h4>Add new item to PeiceGroup</h4>
<%= form_for #price_group, html: {class: "form-inline"} do |f| %>
<div class="form-group">
<label>Item</label>
<%= f.collection_select(:id, Item.all, :id, :name, {}, {class: "form-control"}) %>
</div>
<div class="form-group">
<label>Price</label>
<%= f.text_field :price, class: "form-control" %>
</div>
<%= f.submit "Add item", class: "btn btn-default" %>
<% end %>
</div>
Assuming that you have a variable which has your pricegroup object named #price_group. So on the view what you can do is:
<% #price_group.price_group_lines.each do |price_group_line| %>
<%= price_group_line.name %> #or anything you want to do
<%= price_group_line.item.name %> #for the item name
<% end %>
Suppose you need comma separated values for a single attribute from price_group_line then you can also do it like this:
<%= #price_group.price_group_lines.map(&:name).join(', ') %>
This will give you all the names comma separated.
And as Glupo suggested you can have eager loading like this to avoid db requests on your views. So in your controller action:
#price_group = PriceGroup.find(params[:id]).include(:price_group_lines)
Hope this helps.
I have a list of payment records in Payments#index. I've been asked to make that table directly editable and to have one button that saves all the updates. So far I've only partially managed to do this by wrapping each row around a form_for for that record like so:
<tbody>
<% #payments.each do |payment| %>
<tr>
<td><%= payment.client.trading_name %></td>
<td><%= payment.date_of_payment %></td>
<td>
<%= form_for payment do |f| %>
<%= f.text_field :amount %>
<%= f.submit 'Save', class: 'table_submit' %>
<% end %>
</td>
<td>
<%= form_for payment do |f| %>
<%= f.select :sequence_type, options_for_select(%w[RCUR OOFF FRST FNAL]) %>
<%= f.submit 'Save', class: 'table_submit' %>
<% end %>
</td>
<td>
<ul>
<li><%= link_to 'Edit', edit_payment_path(payment) %></li>
<li><%= link_to 'Destroy', payment, method: :delete, data: { confirm: 'Are you sure?' } %></li>
</ul>
</td>
</tr>
<% end %>
</tbody>
Is it possible to be able to edit all the records then have one update button instead of having to save each record individually? If so, how do I go about doing that?
Its possible:
http://railscasts.com/episodes/165-edit-multiple-revised
And old version
http://railscasts.com/episodes/165-edit-multiple