I have a form that updates two different models using fields_for. The data for the elements inside the fields_for are getting submitted but the ones for the original form_for are missing
Here is the form:
<%= form_for #cart, :remote => true do |cart| %>
<%= fields_for #cart.order, :remote => true do |order| %>
<%= order.select :country, options_for_select(#country_options) %>
<%= order.text_field :zip %>
<% end %>
<%= cart.select :shipping_method,options_for_select(#shipping_options) %>
<% end %>
Here is what is contained in the params in the update action:
{"_method"=>"put", "utf8"=>"\342\234\223", "action"=>"update", "order"=>{"zip"=>"48360", "country"=>"US"}, "id"=>"1", "controller"=>"carts"}
Why is the shipping_method field not appearing in the params?
Here is the generated HTML form:
<form accept-charset="UTF-8" action="/carts/3" class="edit_cart" data-remote="true" id="edit_cart_3" method="post"><div style="margin:0;padding:0;display:inline"><input name="utf8" value="✓" type="hidden"><input name="_method" value="put" type="hidden"></div>
<select id="order_country" name="order[country]"><option value="US">United States</option></select>
<input id="order_zip" name="order[zip]" size="30" value="90001" type="text">
<select id="cart_shipping_method" name="cart[shipping_method]"><option value="FedEx Ground" selected="selected">FedEx Ground: $5.00</option></select>
</form>
I think you should maybe try some thing like this:
<%= cart.select :shipping_method, options_from_collection_for_select(#shipping_options, 'id', 'name') %>
Where 'name' is the parameter you want to display. Otherwise I would suggest inspecting the html of the form. If the select items don't have a value attribute, something is wrong with your erb
options_for_select only work for 2xn arrays
Ok you then I see something else that may cause problems, it think it should be:
<%= cart.fields_for #cart.order, :remote => true do |order| %>
Related
A parent class 'model'
has_many :modelbodycontacts
has_many :bodycontacts, through: :modelbodycontacts
accepts_nested_attributes_for :modelbodycontacts, allow_destroy: true
modelbodycontacts being a join table for model and bodycontact.
The related controller permits
params.require(:model).permit(:name, :modelbodycontact_attributes)
However, using form_with
<%= form_with(model: model, local: true) do |form| %>
<%= form.fields(:modelbodycontact) do |bodycontact_fields| %>
<% #bodycontacts.each do |bodycontact| %>
<%= bodycontact_fields.check_box :bodycontact_id %><%= bodycontact.name %>
<% end %>
<% end %>
generates the following HTML
<input name="model[modelbodycontact][bodycontact_id]" type="hidden" value="0" /><input type="checkbox" value="1" name="model[modelbodycontact][bodycontact_id]" id="model_modelbodycontact_bodycontact_id" /> Seat
<input name="model[modelbodycontact][bodycontact_id]" type="hidden" value="0" /><input type="checkbox" value="1" name="model[modelbodycontact][bodycontact_id]" id="model_modelbodycontact_bodycontact_id" /> Back
submitting data leads to Unpermitted parameter: :modelbodycontact
There are a few errors here:
the bodycontact_id is not being generated in the HTML code, only the checked/unchecked values, thus a child record cannot be properly created
the parameters are not being permitted; particularly, the reference is to the model name, not its attribute(s)
Documentation indicates that "The form_with method automatically includes the model id as a hidden field in the form." Well, it ain't there... But possibly Rails handles that internally knowing it is a nested
One could revert to form_for and its tried and tested ways, but that will be eventuall be deprecated. How can form_with be properly used here?
changing param option in controller
params.require(:model).permit(:name, modelbodycontact_attributes: [])
and
<%= form.fields("modelbodycontact_attributes[]") do |bodycontact_fields| %>
<% #bodycontacts.each do |bodycontact| %>
<%= bodycontact_fields.check_box :bodycontact_id %><%= bodycontact.name %>
<% end %>
<% end %>
should work i think
I have a nested form that is based off of the example in this rails cast:
http://railscasts.com/episodes/196-nested-model-form-revised
For my application, I'm trying to let people upload multiple files in a nested form. Each step can have many design files, and steps accept nested attributes for design files:
Step.rb:
class Step < ActiveRecord::Base
attr_accessible :design_files_attributes
has_many :design_files
accepts_nested_attributes_for :design_files, :allow_destroy => :true
...
end
DesignFile.rb:
class DesignFile < ActiveRecord::Base
belongs_to :step
...
end
I'm running into issues when I try to add an additional file after another file has already been added. This is what the form looks like in html:
<div class="upload_new_files">
<fieldset class="design_file_upload_fieldset">
<label for="step_design_files_attributes_0_design_file_path">test.pdf</label>
<input id="step_design_files_attributes_0__destroy" name="step[design_files_attributes][0][_destroy]" type="hidden" value="false">
<i class="icon-trash"></i> Remove File
<input id="step_design_files_attributes_0_step_id" name="step[design_files_attributes][0][step_id]" type="hidden" value="451">
<input id="step_design_files_attributes_0_project_id" name="step[design_files_attributes][0][project_id]" type="hidden" value="120">
<input id="step_design_files_attributes_0_user_id" name="step[design_files_attributes][0][user_id]" type="hidden" value="15">
<hr>
</fieldset>
<fieldset class="design_file_upload_fieldset">
<input id="step_design_files_attributes_70265135732560_design_file_path" name="step[design_files_attributes][70265135732560][design_file_path]" onchange="javascript: addField(this);" type="file" style="display: none;">
<input id="step_design_files_attributes_70265135732560__destroy" name="step[design_files_attributes][70265135732560][_destroy]" type="hidden" value="false">
<i class="icon-trash"></i> Remove File
<input id="step_design_files_attributes_70265135732560_step_id" name="step[design_files_attributes][70265135732560][step_id]" type="hidden" value="446">
<input id="step_design_files_attributes_70265135732560_project_id" name="step[design_files_attributes][70265135732560][project_id]" type="hidden" value="120">
<input id="step_design_files_attributes_70265135732560_user_id" name="step[design_files_attributes][70265135732560][user_id]" type="hidden" value="15">
<hr>
</fieldset>
</div>
But when I submit the form, the design_files_attributes aren't in the parameters. I'm expecting something like this:
Parameters: {"step"=>{ "design_files_attributes"=>{"0"=>{"id"=>"17" ...}}, "70265135732560"=>{...}...}
But, instead, I'm getting this:
Parameters: {"step"=> {"design_files_attributes"=>{"0"=>{"id"=>"17" ...}}
What isn't my new entry being submitted in the form parameters?
Edit
Here is the full params that are being submitted to the controller; I didn't include it above because it includes a lot of other things, but I have it here in case it's useful:
Parameters: {"utf8"=>"✓", "authenticity_token"=>"...=", "step"=>{"name"=>"New Design File", "description"=>"", "question_attributes"=>{"description"=>"", "decision_attributes"=>{"description"=>"", "question_id"=>""}}, "design_files_attributes"=>{"0"=>{"_destroy"=>"false", "step_id"=>"451", "project_id"=>"120", "user_id"=>"15", "id"=>"17"}}, "last"=>"0", "published_on_date"=>"02/04/2014", "published_on_time"=>"05:02 PM", "timeZone"=>"EST"}, "commit"=>"Update Step", "project_id"=>"120", "id"=>"3"}
_step_form.html.erb
<%= semantic_form_for [#project, #step], :html => {:multipart => true} do |f| %>
<%= f.fields_for :design_files, multipart: true do |design_file_form| %>
<%= render 'design_file_fields', f: design_file_form %>
<% end %>
<p class="add_design_file" style="display:none;"><%= link_to_add_fields raw("<icon class='icon-plus-sign' style='color:green;'></icon> Add Another Design File"), f, :design_files %></p>
<% end %>
_design_file_fields.html.erb
<fieldset class="design_file_upload_fieldset">
<% if f.object.new_record? %>
<%= f.file_field :design_file_path, :onChange=>"javascript: addField(this);" %>
<% else %>
<%= f.label :design_file_path, f.object.design_file_path.to_s.split('/').pop() %>
<% end %>
<%= f.hidden_field :_destroy %>
<%= link_to raw('<i class="icon-trash"></i> Remove File'), "#", class: "remove_fields" %>
<%= f.hidden_field :step_id, :value => #step.id %>
<%= f.hidden_field :project_id, :value => #project.id %>
<%= f.hidden_field :user_id, :value => current_user.id %>
<hr>
</fieldset>
steps.js
// add a new design file field to the form
function addField(input) {
console.log('add design field input');
// remove the filefield of recently uploaded file and replace with existing file styling
var filename = $(input).val().split('\\').pop();
$(input).parent('fieldset').prepend('<label>'+filename+'</label>');
$(input).hide();
$('.add_fields').click();
}
application_helper.rb
def link_to_add_fields(name, f, association)
new_object = f.object.send(association).klass.new
id = new_object.object_id
fields = f.fields_for(association, new_object, child_index: id) do |builder|
render(association.to_s.singularize + "_fields", f: builder)
end
link_to(name, '#', class: "add_fields", data: {id: id, fields: fields.gsub("\n", "")})
end
Wow, the answer ended up being totally unrelated to what I had posted! The issue was with the form itself, which had an extra <% end %> element, causing the design files fields_for not being incorporated into the form. I'll leave this response in case it's useful for anyone else.
I have a working twitter bootstrap install and simple form generates the following:
<form accept-charset="UTF-8" action="/find_map" class="simple_form form-inline" id="new_location" method="post" novalidate="novalidate"><div style="margin:0;padding:0;display:inline"><input name="utf8" type="hidden" value="✓" /><input name="authenticity_token" type="hidden" value="p5CSoidWaoGMfHY0/3ElWi0XJVg6Cqi9GqWRNlJLBQg=" /></div>
<div class="control-group string required"><div class="controls"><input class="string required" id="location_address" name="location[address]" placeholder="Address" size="50" type="text" /></div></div><input class="btn" name="commit" type="submit" value="Find!" />
</form>
Somehow the "Find!" button won't appear on the same line as the search box. Any ideas?
Thanks!
UPDATE:
Sorry I should have mentioned that all the markup is generated by simple_form based on the following:
<%= simple_form_for #location, :url => find_map_path, :html => { :class => 'form-inline' } do |f| %>
<%= f.input :address, :label => false, :placeholder => "Address" %>
<%= f.submit "Find!", :class => 'btn' %>
<% end %>
So, really, there seems to be an issue with the generated markup, even though I have run the bootstrap install for simple_form, etc.
The above image shows a straight html form
<form class="form-inline">
<input type="text" class="input-small" placeholder="Email">
<button type="submit" class="btn">Sign in</button>
</form>
...above the one generated by simple_form.
I think there are a couple issues here. One is the formatting, and the way simple_form adds a <div> around the input field. #Ron's suggestion of using input_field works for me with simple_form 2.0.1. My example is searching for name in a Contacts table. The following makes the text box and button appear side by side:
<%= simple_form_for :contact, :method => 'get',
:html => { :class => 'form-search' } do |f| %>
<%= f.input_field :search, :placeholder => "Name",
:class => "input-medium search-query" %>
<%= f.submit "Find!", :class => "btn" %>
<% end %>
The other issue is that it seems simple_form usually assumes you want to work with model and field names. The example above uses a :symbol instead of a #model as the first argument as suggested here. But that still generates an input field named contact[search] so you'd have to tell your controller how to deal with that.
I think in this case it may be simpler to not use simple_form and instead use something like the form near the beginning of Ryan Bates' Railscast #240, Search, Sort, Paginate with AJAX:
<%= form_tag contacts_path, :method => 'get', :class => "form-search" do %>
<%= text_field_tag :search, nil, :placeholder => "Name",
:class => "input-medium search-query" %>
<%= submit_tag "Find!", :name => nil, :class => "btn" %>
<% end %>
Now the field is just named "search" and I can consume it in my controller's #index method something like this:
#contacts = #contacts.search(params[:search])
assuming I have this in my model:
def self.search(search)
if search
where('lower(name) LIKE ?', "%#{search.downcase}%")
else
scoped
end
end
It's creating subforms because you're passing input to simple_form. Use input_field instead. (BTW, this also works with simple_fields_for).
You need to customize the control-group and controls div classes to display as inline-block when they are under a form-inline form:
form.form-inline div.control-group { display: inline-block; }
form.form-inline div.control-group div.controls { display: inline-block; }
Adding to Mark's reply:
So, input_field exists to create the input component only, it won't give you any sort of label/error/wrapper. That means you won't get any or tag wrapping the field, you should do that manually in case you want to.
Now about using the form with an object, SimpleForm is a FormBuilder, which means it was created to work with a namespace, either an object or a symbol. SimpleForm's simple_form_for == Rails' form_for, you always need an object namespace to work with.
For such a simple case as a search form, you're better off with simple form helpers such as form_tag, as you've pointed out. There's no need to rely on simple_form_for or form_for for that, I agree and I usually go down that path.
I hope that helps, let me know if you still have doubts.
Change the :html => { :class => 'form-inline' } to :html => { :class => 'form-horizontal' }
can't you move the input button next to input address? I think it will solve the problem
<form accept-charset="UTF-8" action="/find_map" class="simple_form form-inline" id="new_location" method="post" novalidate="novalidate">
<div style="margin:0;padding:0;display:inline">
<input name="utf8" type="hidden" value="✓" />
<input name="authenticity_token" type="hidden" value="p5CSoidWaoGMfHY0/3ElWi0XJVg6Cqi9GqWRNlJLBQg=" />
</div>
<div class="control-group string required">
<div class="controls">
<input class="string required" id="location_address" name="location[address]" placeholder="Address" size="50" type="text" />
<!-- move the button to here -->
<input class="btn" name="commit" type="submit" value="Find!" />
</div>
</div>
</form>
Please, all people with this problem, don't use fluid layout and be sure you are specifying the HTML5 doctype to the documents.
I would like to use the form_for helper multiple times for the same model in the same page. But the input fields use the same ID attribute (in the HTML), so clicking on the label of a field in another form will select the same input in the first form.
Is there a solution besides settings all attributes manually via :for => "title_#{item.id}" and :id => "title_#{item.id}"?
Using Rails 3.0.9
You can use :namespace => 'some_unique_prefix' option. In contrast to :index, this will not change the value used in the name attribute.
It's also possible to use an array, e.g. when you have nested forms or different forms that happen to have some fields in common: :namespace => [#product.id, tag.id] or :namespace => [:product, #product.id]
I found the answer myself, one can pass a :index option to form_for. That string will be used in the id and for attributes:
<%= form_for #person, :index => #person.id do |f| %>
<%= f.label :name %>
<%= f.text_field :name %>
<%= f.submit %>
<% end %>
will parse
<form accept-charset="UTF-8" action="/person/11" class="edit_person" id="edit_person_11" method="post">
<!-- Hidden div for csrf removed -->
<label for="person_11_name">Name</label>
<input id="person_11_name" name="person[11][name]" size="30" type="text" />
<input name="commit" type="submit" value="Update Person" />
</form>
Notice it'll change the name of the inputs as well.
I believe you can add this param:
:html => { :id => 'id_i_want' }
another day with Rails and today I want to use Ajax. linkt_remote_link for changing a text was pretty easy so I thought it would also be easy to switch my form_for loop just to an ajax request form with remote_form_for, but the problem with the remote_form_for is that it doesn't save my changes?
Here the code that worked:
<% form_for bill, :url => {:action => 'update', :id => bill.id} do |f| %>
# make the processing e.g. displaying texfields and so on
<%= submit_tag 'speichern'%>
It produces the following html code:
<form action="/adminbill/update/58" class="edit_bill" id="edit_bill_58" method="post"><div style="margin:0;padding:0;display:inline"><input name="_method" type="hidden" value="put" /></div>
<!-- here the html things for the forms -->
<input class="button" name="commit" type="submit" value="speichern" />
Here the code which don't save me the changes and submit them:
<% remote_form_for bill, :url => {:action => 'update', :id => bill.id} do |f| %>
# make the processing e.g. displaying texfields and so on
<%= submit_tag 'speichern'%>
It produces the following html code:
<form action="/adminbill/update/58" class="edit_bill" id="edit_bill_58" method="post" onsubmit="$.ajax({data:$.param($(this).serializeArray()), dataType:'script', type:'post', url:'/adminbill/update/58'}); return false;"><div style="margin:0;padding:0;display:inline"><input name="_method" type="hidden" value="put" /></div>
<!-- here the html things for the forms -->
<input class="button" name="commit" type="submit" value="speichern" />
I don't know if I have to consider something special when using remote_form_for (see remote_form_for)
Try this
<% remote_form_for :bill, bill, :url => {:action => 'update', :id => bill.id} do |f| %>
OR
<% remote_form_for bill, bill, :url => {:action => 'update', :id => bill.id} do |f| %>
EDITED.
I think problem is with your response your 'update' method should be something like following
def update
#bill = Bill.find(params[:id])
if #bill.update_attributes(params[:bill])
render :update |page|
page.replace_html "some_div", :partial=>'some_partial', :object=>[#bill]
page.replace_html "notice_div", "Bill updated succesfully"
end
else
render :update |page|
page.replace_html "some_div", :partial=>'some_partial', :object=>[#bill]
page.replace_html "notice_div", ""
end
end
end