How to save form data in rails dynamically - ruby-on-rails

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?

Related

ActionController::ParameterMissing and multi select rows in a table

I am trying to create a button in View that will create a new record and display in another controller and then display that View.
However I get this error when I click that button.
ActionController::ParameterMissing in JobHeadersController#create
param is missing or the value is empty: job_header
# Never trust parameters from the scary internet, only allow the white list through.
def job_header_params
params.require(:job_header).permit(:company_id, :customer_id, :name, :del_address1, :del_address2, :del_address3, :del_address4, :del_postcode, :tel, :created_by)
end
end
Parameters:
{"utf8"=>"✓",
"authenticity_token"=>"BLAHBLAHBLAHg==",
"company_id"=>"2",
"customer_id"=>"1",
"name"=>"Nash Labs",
"del_address1"=>"23 Taylor Road",
"del_address2"=>"Odsal",
"del_address3"=>"Bradford",
"del_address4"=>"West Yorkshire",
"del_postcode"=>"BD6 1BH",
"tel"=>"07522189605",
"created_by"=>"2",
"button"=>""}
My View code is
<%= form_tag({controller: "job_headers", action: "create"}, method: "post") %>
<% #other_assets.each do |assets| %>
<tr>
<td><%= check_box_tag 'selected_assets[]', assets.id%></td>
<td><%= assets.service_type %></td>
<td><%= assets.asset_type %></td>
<td><%= assets.make %></td>
<td><%= assets.model %></td>
<td><%= assets.serial_no %></td>
<td><%= format_date(assets.date_next) %></td>
<td><%= link_to "Notes", customer_asset_service_register_path, :id => 'notesModal', "data-toggle" => "modal", 'data-target' => '.bs-example-modal-lg' %></td>
</tr>
<% end %>
</table>
</div>
<%= hidden_field_tag :company_id, #customer.company_id %>
<%= hidden_field_tag :customer_id, #customer.id %>
<%= hidden_field_tag :name, #customer.name %>
<% if #customer.del_address1.blank? %>
<%= hidden_field_tag :del_address1, #customer.address1 %>
<% else %>
<%= hidden_field_tag :del_address1, #customer.del_address1 %>
<% end %>
<% if #customer.del_address1.blank? %>
<%= hidden_field_tag :del_address2, #customer.address2 %>
<% else %>
<%= hidden_field_tag :del_address2, #customer.del_address2 %>
<% end %>
<% if #customer.del_address1.blank? %>
<%= hidden_field_tag :del_address3, #customer.address3 %>
<% else %>
<%= hidden_field_tag :del_address3, #customer.del_address3 %>
<% end %>
<% if #customer.del_address1.blank? %>
<%= hidden_field_tag :del_address4, #customer.address4 %>
<% else %>
<%= hidden_field_tag :del_address4, #customer.del_address4 %>
<% end %>
<% if #customer.del_address1.blank? %>
<%= hidden_field_tag :del_postcode, #customer.postcode %>
<% else %>
<%= hidden_field_tag :del_postcode, #customer.del_postcode %>
<% end %>
<%= hidden_field_tag :tel, #customer.tel %>
<%= hidden_field_tag :created_by, current_user.id %>
<div class="box-tools pull-right">
<%= button_tag(type: "submit", class: "btn btn-primary pull_right") do %>
<i class='fa fa-calendar'></i> Create Job
<% end %>
<% end %>
If I am reading the error right it is expecting the param "job_header" and it is null however, this is to create a new job_header record and so I can't pass that param?
If anyone also knows how I Can put a tick box against my table rows so that the selected #other_asset id's get passed to a param as an array please also let me know. It will save me lots of googling.
Thanks in advance
ActionController::ParameterMissing in JobHeadersController#create
param is missing or the value is empty: job_header
You are using a form_tag to submit the data, so there won't be any key(model instance) to which the params append. You just need to change
params.require(:job_header).permit(:company_id, :customer_id, :name, :del_address1, :del_address2, :del_address3, :del_address4, :del_postcode, :tel, :created_by)
to
params.permit(:company_id, :customer_id, :name, :del_address1, :del_address2, :del_address3, :del_address4, :del_postcode, :tel, :created_by)
Rails way: Use form_for #job_header do |f|
to create a form scoped by #job_header and in job_header_params will be:
{"utf8"=>"✓", "authenticity_token"=>"BLABLABLA",
"job_header": {
"company_id"=>"2",
"customer_id"=>"1",
"name"=>"Nash Labs",
"del_address1"=>"23 Taylor Road",
"del_address2"=>"Odsal",
"del_address3"=>"Bradford",
"del_address4"=>"West Yorkshire",
"del_postcode"=>"BD6 1BH",
"tel"=>"07522189605",
"created_by"=>"2"},
"button"=>""}
The exception is saying that the required node (:job_header) is missing and can find the permitted params (params['job_header'] => nil).
More at https://api.rubyonrails.org/v5.1/classes/ActionView/Helpers/FormHelper.html
Pay attention to these items that you didn't get it:
https://edgeapi.rubyonrails.org/classes/ActionController/StrongParameters.html
https://guides.rubyonrails.org/form_helpers.html
And if you already have the information for those hidden fields (in #customer), why you send to the view to receive the """same""" information back from it? (The same with " because a user can alter this data with a page inspection and send maliciously code...)
Only fill these attrs on the controller.

How do I submit 3 forms with 1 submit button?

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.

Form_tag routing error after unsuccessful submit

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?

Passing a value from one view to another

I have a button on an index page that links to a new_assignment_path
<% #users.each do |user| %>
<tr>
<td><%= link_to user.name, user %></td>
<td><%= link_to 'Assign to Class', new_assignment_path, :class => 'btn btn-mini' %></td>
</tr>
<% end %>
And I want it so that when you click on it, it takes you to the new_assignment_path, and takes the dropdown select on that pages form.
<%= simple_form_for(#assignment) do |f| %>
<%= f.error_notification %>
<div class="form-inputs">
<%= f.input :user_id, collection: User.all.collect, as: :select %>
</div>
<div class="form-actions">
<%= f.button :submit %>
</div>
<% end %>
And have the drop down automatically set to the user.id of whatever user the button was inside of.
What is the best way of doing this?
Add params[:user_id] in the selected option of your select tag
<div class="form-inputs">
<%= f.input :user_id, collection: User.all.collect, as: :select, :selected => params[:user_id] %>
</div>

Rails 3 submit a form with multiple records

I'm new to rails so this is probably a basic question. I am trying to create a form where the user can create 3 records at once. I want the user to only have to click the submit button once. I'm submitting to my Review model a name, comment, and rating. Currently, only the last record is entered into the database.
<%= form_for([#user,#review]) do |f| %>
<table>
<tr>
<td>Rank</td>
<td>Name</td>
<td>Comment</td>
</tr>
<tr>
<td>1</td>
<td><%= f.text_field :name %></td>
<td><%= f.text_field :comment %></td>
<%= f.hidden_field :rating, :value=> "5" %>
</tr>
<tr>
<td>2</td>
<td><%= f.text_field :name %></td>
<td><%= f.text_field :comment %></td>
<%= f.hidden_field :rating, :value=> "3" %>
</tr>
<tr>
<td>3</td>
<td><%= f.text_field :name %></td>
<td><%= f.text_field :comment %></td>
<%= f.hidden_field :rating, :value=> "1" %>
</tr>
</table>
<div class="actions">
<%= f.submit "Create my top 3" %>
</div>
<% end %>
Any advice is appreciated. Thanks.
I would recommend using fields_for for this:
<%= form_for([#user, :reviews]) do |f| %>
<% #reviews.each do |review| %>
<%= fields_for review do |r| %>
<%= render "reviews/form", :r => r %>
<% end %>
<% end %>
<% end %>
To make this work, you will need to build as many review objects as you require in your controller:
def new
# you could also have this in a before_filter...
#user = User.find(params[:id])
#reviews = Array.new(3) { #user.reviews.build }
end
This would create new instances of review records for this user, which is different from new records. Instances are simply Ruby objects. Now because you've called #user.reviews.build three times, you'll see three reviews in your view.
def create
#user = User.find(params[:id])
#reviews = Review.create(params[:reviews])
# Some more logic for validating the parameters passed in
end
This will create three new Review objects and link them to #user, assuming all three are valid.
You'll need to tell rails its an array. First, read this section of this article:
For your purpose, you'll need to build the form by hand:
<%= form_tag 'foo' do %>
<% [1,3,5].each do |i| %>
<%= text_field_tag 'review[][name]' %>
<%= text_field_tag 'review[][comment]' %>
<%= hidden_field_tag 'review[][rating]', :value => i %>
<% end %>
<% end %>

Resources