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
Related
I am trying to make a form where you select tables that you want to export. I made a simple form with a list of tables that can be exported. My plan was to allow the user to toggle check boxes for the tables they want to export and as a result they would be able to download a zip file containing the tables.
Currently, when I try to go to the page with the form, I get an error:
undefined method 'model_name' for nil:NilClass
The majority of the usage of simple forms that I see online consists of using forms to create new items to save in their models. As a result, it seems that the line simple_form_for #example would mean that when the user clicks the submit button, there is a line in the controller such as #example = SomeClass.new". My understanding is that the user input of the form is saved in #example and can be used by the controller. However, as I am not creating a new item in the model, I just want to use the values from #example, I am not sure what to put in the controller to get rid of the error so that I can code the rest of the function in the controller.
Controller:
class FormController < ApplicationController
def index
#options = []
print(#options)
end
end
The form used:
<h2>Which tables do you want to export?</h2>
<div class="well">
<% tables_in_model = %w(Table1 Table2 Table3) %>
<%= simple_form_for #selected_options, :url => form_index_path, :method => :get do |f| %>
<%= f.input :options, as: :check_boxes, collection: tables_in_model %>
<%= f.button :submit, as: :Submit %>
<% end %>
</div>
As you said correctly in your question, simple_form should be used to render forms to the user when her actions are related to the creation or edition of ActiveRecord models.
For instance, when writing down code to enable a search feature, where your goal is to simply pass a bunch of user chosen params to a controller, you should not use it. I believe you are in a similar position with the feature you described.
Simple solution though: use rails form related DSL to get your form going!
Hope it's the answer you needed. Feel free to ask for more details if needed. Cheers!
i think you are using simple_form you need to specify like
#selected_options = SelectedOptionModel.new(params)
into your controller
then it passes into View.
if you don't have any model you can use form_tag
like this:
<%= form_tag("/search", method: "get") do %>
<%= label_tag(:q, "Search for:") %>
<%= text_field_tag(:q) %>
<%= submit_tag("Search") %>
<% end %>
this will create html form like this:
<form accept-charset="UTF-8" action="/search" method="get">
<input name="utf8" type="hidden" value="✓" />
<label for="q">Search for:</label>
<input id="q" name="q" type="text" />
<input name="commit" type="submit" value="Search" />
</form>
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 the following model setup. I have Match and Game. Match has_many :games, and Match accepts_nested_attributes_for :games. :games_attributes is attr_accessible by all members. My problem is, fields_for is not generating the correct form, so Match is not accepting the nested attributes for games. Here is my form code:
<%= form_for [#tournament, match], url: tournament_match_path(#tournament, match) do |f| %>
<%= f.fields_for match.games.last do |builder| %>
<%= builder.hidden_field :winner_id, value: 1 %>
<% end %>
<%= f.submit "Win Game", class: "actionButton activeAction" %>
<% end %>
The code generated by this is:
<form id="edit_match_1" class="edit_match" method="post" action="/tournaments/1/matches/1" accept-charset="UTF-8">
<div style="margin:0;padding:0;display:inline"> <!-- rails stuff here --></div>
<input id="match_game_winner_id" type="hidden" value="1" name="match[game][winner_id]">
<input class="actionButton activeAction" type="submit" value="Win Game" name="commit">
</form>
As you can see, the name of match_game_winner_id is incorrect. The name is match[game][winner_id], but the name should be match[games_attributes][0][winner_id]. How can I solve this problem?
I ended up solving this question on my own, and would like to share my findings to help others in a similar situation. As Andrew said in the comments, fields_for expects a symbol, so in order to get the last game, I need to pass a list of symbols corresponding to the methods that get the last game. My form ended up looking like this:
<%= form_for [#tournament, match], url: tournament_match_path(#tournament, match) do |f| %>
<%= f.fields_for :games, :last do |builder| %>
<%= builder.hidden_field :winner_id, value: 1 %>
<% end %>
<%= f.submit "Win Game", class: "actionButton activeAction" %>
<% end %>
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| %>
I need to create a multi-edit form in rails, like so:
<form>
<input type='text' name='input1'></input>
<input type='text' name='input2'></input>
<input type='text' name='input3'></input>
<input type='text' name='input4'></input>
<input type='text' name='input5'></input>
<br>
<input type='text' name='input1'></input>
<input type='text' name='input2'></input>
<input type='text' name='input3'></input>
<input type='text' name='input4'></input>
<input type='text' name='input5'></input>
<br>
<input type='text' name='input1'></input>
<input type='text' name='input2'></input>
<input type='text' name='input3'></input>
<input type='text' name='input4'></input>
<input type='text' name='input5'></input>
<br>
... and so on, then the "<submit>" button will be at the very end. One click of the submit button at the end should collect all the values and parse them in the controller.
I just need to know how to generate the multi-edit form in the view. Also, each row is unique; I'd also need to know how to assign a unique identifier to each of the input tags I guess; I do have a unique ID value I could use.
This is trivial to accomplish, but we need more information. How are these fields related to your models? Is this one model with many fields, many instances of a model or something else?
What you want to do in this situation is use a form builder. It will generate input fields according to a naming convention that will be parsed into a much more useful format when it gets to the controller. Since I have no information about your models, I will use a hypothetical example:
class Post < ActiveRecord::Base
attr_accessible :title, :body, :author, :published_at
end
Create the form using the form_for helper. It will give you a formbuilder object to create the input fields.
<% form_for :post do |f| -%>
<p>
<%= f.label :title %>
<%= f.text_field :title %>
</p>
<p>
<%= f.label :body %>
<%= f.text_area :body %>
</p>
<p>
<%= f.label :author %>
<%= f.text_field :author %>
</p>
<p>
<%= f.label :published_at %>
<%= f.datetime_select :published_at %>
</p>
<% end -%>
The key benefit of using helpers is the name attribute of the inputs it generates. Since body belongs to a form for post it will be given the name attribute post[body]. These attributes will be parsed into the following hash:
:post => {
:title => "This is the title",
:body => "this is the body",
:author => "John Doe",
:published_at => "Mon Nov 15 2010 19:23:40 GMT-0600 (CST)"
}
This means you don't need to manually copy fields into a model. You can just pass it directly to the Model#new method:
#post = Post.new(params[:post])
and then do your validation checks. This convention becomes indispensable when you start nesting models inside one another.
See here for a more thorough guide to form helpers.