ActionController::ParameterMissing and multi select rows in a table - ruby-on-rails

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.

Related

How to save form data in rails dynamically

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?

Rails form_for only submitting last input

Im trying to create inputs by looping each product and have one form submission for all the inputs. Currently, the form only submits the last input. How would I make it so all the inputs get submitted?
<%= form_for :inventory do |f| %>
<% #products.each do |product| %>
<tr>
<td><%= product.name %></td>
<td><%= product.measurement %></td>
<td><%= f.number_field :amount, class: 'form-control' %></td>
<%= f.hidden_field :product_id, :value => product.id %>
</tr>
<% end %>
<%= f.submit %>
<% end %>
I dont know your use case but you can use the gem cocoon for doing that. There will also be a link to add/remove another product.
https://github.com/nathanvda/cocoon
You can iterate with each_with_index:
<%= form_for :inventory do |f| %>
<% #products.each_with_index do |product, i| %>
<tr>
<%= f.hidden_field "product_id[#{i}]", :value => product.id %>
</tr>
<% end %>
<%= f.submit %>
<% end %>
Like that the output is not very elegant (something like "product_id"=>{"0"=>"1", "1"=>"2", "2"=>"3"... }) but it's for the example to show how every hidden field needs a unique key.
You can define your params in a better way to use them in the controller, just keep them unique.

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: has_many :through _ids nested?

On a single page I might have a single Video and then checkboxes to add multiple dogs to it. Easy enough (as follows)...
View:
<%= check_box_tag "video[dog_ids][]", dog.id %>
Controller:
params[:video][:dog_ids] ||= []
But what I can't figure out how to do is have multiple videos, each with multiple dogs.
I currently have this:
<% #videos.each do |video| %>
<%= fields_for "item[]", video do |f| %>
<%= f.hidden_field :id, :index => nil %>
<%= f.text_field :title, :index => nil%>
<%= f.text_area :body, :index => nil %>
<% video.dogs.each do |dog| %>
<%= check_box_tag "item[][video[dog_ids][]]", dog.id %>
<% end %>
<% end %>
<% end %>
But when I do that, dogs_ids is always nil when it's submitted.
Any idea what I'm doing wrong?
Such a setup with fields_for "item[]" and f.text_field :title, :index => nil produces:
<input id="item__name" name="item[][title]" size="30" type="text" value="vid1">
This indicates that the checkbox name should be item[][dog_ids][].
A working example:
<% #videos.each do |video| %>
<%= fields_for "item[]", video do |f| %>
<%= f.text_field :title, :index => nil %>
<% video.dogs.each do |dog| %>
<%= check_box_tag "item[][dog_ids][]", dog.id %> <%= Dog.name %>
<% end %>
<br>
<% end %>
<% end %>
This produces result in params:
{"item"=> [
{"title"=>"vid1", "dog_ids"=>["1", "2"]},
{"title"=>"vid2", "dog_ids"=>["2"]},
{"title"=>"vid3", "dog_ids"=>["2", "3"]}
]}

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