I am trying to create a form for a nested resource. Here is my route:
map.resources :websites do |website|
website.resources :domains
end
Here are my attempts and the errors:
<% form_for(#domain, :url => website_domains_path(#website)) do | form | %>
<%= form.text_field :name %>
# ArgumentError: wrong number of arguments (1 for 0)
# form_helper.rb:290:in 'respond_to?'
# form_helper.rb:290:in 'apply_form_for_options!'
# form_helper.rb:277:in 'form_for'
<% form_for([#website, #domain]) do | form | %>
<%= form.text_field :name %>
# ArgumentError: wrong number of arguments (1 for 0)
# form_helper.rb:290:in 'respond_to?'
# form_helper.rb:290:in 'apply_form_for_options!'
# form_helper.rb:277:in 'form_for'
<% form_for(:domain, #domain, :url => website_domains_path(#website)) do | form | %>
<%= form.text_field :name %>
# ArgumentError: wrong number of arguments (1 for 0)
# wrapper.rb:14:in 'respond_to?'
# wrapper.rb:14:in 'wrap'
# active_record_helper.rb:174:in 'error_messages_for'
<% form_for(:domain, [#website, #domain]) do | form | %>
<%= form.text_field :name %>
# UndefinedMethodError 'name' for #<Array:0x40fa498>
I have confirmed both #website and #domain contain instances of the correct class.
The routes also generate correctly is used like this for example, so I dont think their is an issue with the route or url helpers.
<%= website_domains_path(1) %>
<%= website_data_source_path(1, 1) %>
Rails 2.3.5
You can use
<%= form_for [#website, #domain] %>
Where #domain belongs to #website
I've just tried:
<% form_for #note, :url => teams_person_notes_path(#person) do |f| %>
<%= f.text_field :note %>
<%= f.submit "Add" %>
<% end %>
and it works without any problem. My routes looks like this:
map.namespace :teams do |t|
t.resources :people do |p|
p.resources :notes
end
end
So it is the same as yours (only namespace is added, but it's not a case).
Of Course, your example form is for new object of #domain. It won't work for edit action, then you should have:
<% form_for #domain, :url => edit_website_domain_path(#website, #domain) do |form| %>
This was so obvious when it finally clicked, I had a field called respond_to. It was of course overriding an existing method.
Its a shame ActiveRecord does not warn when a database column will overwrite an already defined method...
Related
Issue: I have a nested fields_for text_field not appearing, I am not sure what I have been done wrong.
Goal: While creating a record, iterate through a model with preset variables, and save a file (testing with text_field) to a join table which saves both the preset variables and the forms record ID
Models:
class PrintLocation < ApplicationRecord
has_many :shop_products, through: :shop_product_print_files
has_many :shop_product_print_files
accepts_nested_attributes_for :shop_product_print_files
end
class ShopProductPrintFile < ApplicationRecord
belongs_to :shop_products
belongs_to :print_locations
end
class ShopProduct < ApplicationRecord
...
has_many :shop_product_print_files
has_many :print_locations, through: :shop_product_print_files
accepts_nested_attributes_for :print_locations
accepts_nested_attributes_for :shop_product_print_files
...
end
Form:
<%= form_for #shop_product do |f| %>
<%= f.collection_select :product_id, #products, :id, :sku %>
<% PrintLocation.all.each do |print_location| %>
<%= print_location.title %>
<%= f.fields_for :shop_product_print_files do |a| %>
<%= a.text_field :print_file %>
<% end %>
<% end %>
<%= f.submit %>
<% end %>
With this, the text_field doesn't appear but the print_location.title's do appear. There are no errors with this.
While saving the #shop_product, I want to be able to iterate through the possible print_location variables, which are defined, and then for each possible print_location, to then be able to upload a file (text_field for testing), and then save that to the ShopProductPrintFile model which has shop_product_id and print_location_id and print_file attributes.
Is there something I am misunderstanding for how to use fields_for?
Shop Product Controller:
Create:
#shop_product = ShopProduct.new(shop_product_params)
shop = Shop.find(params["shop_product"]["shop_id"])
product = Product.find(params["shop_product"]["product_id"]) #shop_product.product_id = product.id
#shop_product.shop_id = shop.id
respond_to do |format|
if #shop_product.save!
...
Update:
#shop_product = ShopProduct.find_by(store_variant_id: params["shop_product"]["store_variant_id"])
#product = Product.find(params["shop_product"]["product_id"])
Strong Params:
def shop_product_params
params.require(:shop_product).permit(:product_id, :store_product_id, :shop_id, :store_variant_id, :sync, :shop_product_print_file_attributes[:id, :print_files, :print_location_ids => [], :shop_product_ids => []], {print_location_ids: []})
end
UPDATE 2:
Update and Create Method:
#shop_product.shop_product_print_files.build
form:
<% PrintLocation.all.each do |print_location| %>
<%= print_location.title %>
<%= f.fields_for :shop_product_print_files_attributes do |a| %>
<%= a.text_field :print_file %>
<%= a.hidden_field :print_location_id, value: print_location.id %>
<%= a.hidden_field :shop_product_id, value: shop_product.id %>
<% end %>
<% end %>
params:
def shop_product_params
params.require(:shop_product).permit(:shop_product_print_files_attributes => [:ids => [], :print_files => [], :print_location_ids => [], :shop_product_ids => []])
end
error:
Shop product print files shop products must exist
Shop product print files print locations must exist
params that pass:
Parameters: {"utf8"=>"✓", "authenticity_token"=>"u/c103465uNCjF/trYrMleqxJ8b9wyLbU/vjPK4llYtCg/ODj92q5MN24==", "shop_product"=>{"sync"=>"1", "product_id"=>"3", "shop_product_print_files_attributes"=>{"print_file"=>"", "print_location_id"=>"6", "shop_product_id"=>"42"}, "store_product_id"=>"191234345", "store_variant_id"=>"15341234273", "id"=>"42"}, "commit"=>"Sync", "id"=>"42"}
The models haven't changed.
Print file in params still blank?
UPDATE 3:
**using this form: thanks to #arieljuod **
<%= f.fields_for :shop_product_print_files do |ff| %>
<%= ff.object.print_location.title # get the print location from the association %>
<%= ff.hidden_field :print_location_id # save the print_location_id as a hidden field %>
<%= ff.file_field :print_file # file input %>
<% end %>
with this in the new and method housing the view:
#shop_product = ShopProduct.new
PrintLocation.all.each{|p| #shop_product.shop_product_print_files.build(print_location: p)}
works on create.
Issue still arises due to not knowing the ID of the ShopProduct until the page loads due to API and there is a possibility of being multiple IDs on one page.
I use:
<% if #shop_products.find_by(store_variant_id: variant.id) %>
<% shop_product = #shop_products.find_by(store_variant_id: variant.id) %>
<%= form_for shop_product do |f| %>
...
Which, variant comes from a loop defined by an API:
<% #in_store_variants.each do |variant| %>
Now when using shop_products (from when shop_product already exists from finding by the variant.id), the fields_for won't appear. Assuming this is because no records exist in relation. Only if a shop_product.shop_product_print_files exist, will they appear.
The only work around, at this time to my knowledge, is to save all print_locations but use a boolean for which are actually active, or search for which print_locations have an ID attached. But i would rather not do it that way and just save which print_locations are chosen on create (chosen by uploading a print_file).
To "fix" this issue, I:
added accepts_nested_attributes_for reject_if: proc { |attributes| attributes['print_file'].blank? } which doesn't save ShopProductPrintFile's unless the print_file field is submitted with something...
use this form (2 forms depending on if exists or not)
<% if #shop_products.find_by(store_variant_id: variant.id) %>
<%= form_for shop_product do |f| %>
<% PrintLocation.all.each{|p| shop_product.shop_product_print_files.build(print_location: p)} %>
<%= f.fields_for :shop_product_print_files do |ff| %>
<%= ff.object.print_location.title %>
<%= ff.hidden_field :print_location_id %>
<%= ff.text_field :print_file %>
<% end %>
<%= f.submit "Sync" %>
<% end %>
<% else %>
<%= form_for #shop_product do |f| %>
<% PrintLocation.all.each{|p| #shop_product.shop_product_print_files.build(print_location: p)} %>
<%= f.fields_for :shop_product_print_files do |ff| %>
<%= ff.object.print_location.title %>
<%= ff.hidden_field :print_location_id %>
<%= ff.text_field :print_file %>
<% end %>
...
The issue with 2 is i have have PrintLocation 1,2,3 associated, it will show 9 fields, the 1,2,3 ready for update, and the 6 ready for create.
is it possible to call the PrintLocation.all.each{|p| #shop_product.shop_product_print_files.build(print_location: p)} on already created ShopProducts's for where a shop_product_print_file doesn't exist in relation to the possible print location.
So for example...
Created ShopProduct with print location, 1,2,3 (out of 6 possible)
Now, shop_product_print_location where print_location exists will show for updating in the form, so thats 1,2, and 3. How can I have it so the other 3 that weren't created now show to update the ShopProduct and create new ShopProductPrintFile's? so it is possible to update the ShopProduct to have more print_locations to the shop_product_print_file model.
I have a nested fields_for text_field not appearing, I am not sure
what I have been done wrong.
You should add this line in your create action
#shop_product = ShopProduct.new(shop_product_params)
#shop_product.shop_product_print_files.build #this one
Also change shop_product_print_file_attributes to shop_product_print_files_attributes to avoid any further errors.
You have to tell rails which PrintLocation to use on each iteration since your object does not have any
<%= f.fields_for :shop_product_print_files, print_location do |a| %>
I'm not really sure if that's what you want, but the field will appear.
EDIT: so, I think you need something like this:
On the controller
#shop_product = something_to_get_the_product
PrintLocation.all.each{|p| #shop_product.shop_product_print_files.build(print_location: p)}
I prefer to do this here, I don't like that logic on the view
Now you have all the possible print location prebuilt on the shop product object
On the form
# note here the multipart option to allow files
<%= form_for #shop_product, multipart: true do |f| %>
<%= f.collection_select :product_id, #products, :id, :sku %>
<%= f.fields_for :shop_product_print_files do |ff| %>
<%= ff.object.print_location.title # get the print location from the association %>
<%= ff.hidden_field :print_location_id # save the print_location_id as a hidden field %>
<%= ff.file_field :print_file # file input %>
<% end %>
<%= f.submit %>
<% end %>
I need to get an integer(#integer) from the form in my root_path, do multiplication (#integer*45) and display the result on the same page. How can I do it without any models in my application?
Please, share your best practice. Thank you!
I was trying to do next:
CalculatorsController
def calculation
#integer = params[:integer]
#result = #integer*45
end
def result
end
root.rb
root :to => 'calculators#result'
resources :calculators, :collection=>{:result => :get, :calculation => :post}
calculators/result.html.erb
<% form_tag root_path, :html => {:method => :post} do %>
<%= label_tag 'integer' %>
<%= text_field_tag :integer %>
<div><%= submit_tag 'OK' %></div>
<% end %>
I'll do it with ajax, so there is no need for page refresh:
First, update the routes, for your example you only need two routes, one get (or root) and one post.
routes.rb:
Rails.application.routes.draw do
root 'calculators#result'
post 'calculators/calculation'
end
Next, update your view:
Change the url in your form_tag where the data will be sent (to calculation action instead of result).
Add remote: true option to enable ajax.
Add a tag where you will display your result.
result.html.erb:
<% form_tag calculators_calculation_url, remote: true do %>
<%= label_tag 'integer' %>
<%= text_field_tag :integer %>
<div><%= submit_tag 'OK' %></div>
<% end %>
<div id="total"></div>
And create a view for calculation action, but since you are using ajax, you will create it as js.erb and include the required javascript (or jQuery) to update your view (i'm using jQuery in the example).
calculation.js.erb:
$('#total').html('<%= #result %>')
Now when you click submit, your form will be sent to calculation action and will update the div with #result.
Just add the field to your form...
<% form_tag root_path, :html => {:method => :post} do %>
<%= label_tag 'integer' %>
<%= text_field_tag(:integer, #integer) %>
<% if #result.present? %>
<br>
Result is: <%= #result %>
<br/>
<% end %>
<div><%= submit_tag 'OK' %></div>
<% end %>
And then render result in your calculate...
def calculation
#integer = params[:integer].to_i
#result = #integer*45
render :result
end
Your result view (result.html.erb) is getting its data from the result method, not calculation. Update your controller as follows:
def calculation
#integer = params[:integer]
end
def result
#result = #integer*45
end
You then need a tag to display your result in the view, something like:
<p> <%= #result %> </p>
My starting place was this discussion: Syntax for form_for when building an array from checkboxes
I have a call to my model passing back an array of valid options. This array then makes a series of check_box_tag
<%= form_for #game, :url => wizard_path do |f| %>
<div>
<% #game.select_races.each do |a| %>
<%= f.label a %>
<%= check_box_tag 'game[races][]', a , true %>
<% end %>
</div>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
This successfully creates an array called 'races' containing the desired output. The problem is that it doesn't actually update the races attribute. So my races attribute is still nil.
I'm sure this is a painful Rails beginner question. Any help is appreciated.
UPDATE
My allowed params were:
def game_params
params.require(:game).permit(:shattered_empire, :shards_of_the_throne, :number_of_players, :rules, :strategy_cards, :players, :races)
end
Which needed to be updated to:
def game_params
params.require(:game).permit(:shattered_empire, :shards_of_the_throne, :number_of_players, :rules, :strategy_cards, :players, {:races => []})
end
Is it possible to pass the value of checked check_box_tags within a form_for in Rails inside a hash?
Here is a very generic, basic version of the form:
<%= form_for(:object, :url => { :action => 'create', :id => params[:id]}) do |f| %>
<p>Field_a: <%= f.text_field(:field_a) %></p>
<p>Field_b: <%= f.text_field(:field_b) %></p>
<p>Field_c: <%= f.text_field(:field_c) %></p>
<p>Check boxes:</p>
<% check_box_choices_object_array.each do |s| %>
<%= check_box_tag(s.name, 1, false) %>
<%= .name %><br />
<% end %>
<%= submit_tag("Create") %>
<% end %>
Outputs roughly:
Field_a ___________________
Field_b ___________________
Field_c ___________________
Check boxes:
[] box_a
[] box_b
[] box_c
[] box_d
[] box_e
[] box_f
[] box_g
My problem is that since the available check boxes aren't actual fields in the object's table in the database (i.e. I'm not using check_box(:field) in the form), each checked check box gets passed as an individual parameter (i.e. "box_a" => "1", "box_b" => "1", "box_e" => "1"). I would like them to be passed as such:
:checked_boxes => {"box_a" => "1", "box_b" => "1", "box_e" => "1"}
This way, I can access them easily with params[:checked_boxes].
How do I do this, or, better yet, is there a better solution (I'm new to rails)?
I think you'd get the results you want if you wrap the checkboxes iterator in a fields_for :checked_boxes tag - or at least get you close to the results you want.
<%= form_for(:object, :url => { :action => 'create', :id => params[:id]}) do |f| %>
<p>Field_a: <%= f.text_field(:field_a) %></p>
<p>Field_b: <%= f.text_field(:field_b) %></p>
<p>Field_c: <%= f.text_field(:field_c) %></p>
<p>Check boxes:</p>
<%= f.fields_for :checked_boxes do |cb| %>
<% check_box_choices_object_array.each do |s| %>
<%= cb.check_box(s.name, 1, false) %>
<%= .name %><br />
<% end %>
<% end %>
<%= submit_tag("Create") %>
<% end %>
you can deal with no database attributes and models using attr_accessor
class Thing < ActiveRecord::Base
attr_accessible :name
attr_accessor :box_a, :box_b, :box_c
end
This way you can call these attributes in your form.
On the front page of my rap lyrics explanation site, there's a place where users can try explaining a challenging line:
alt text http://dl.dropbox.com/u/2792776/screenshots/2010-02-06_1620.png
Here's the partial I use to generate this:
<div class="stand_alone annotation" data-id="<%= annotation.id %>">
<%= song_link(annotation.song, :class => :title) %>
<span class="needs_exegesis"><%= annotation.referent.strip.gsub(/\n/, "\n <br />") %></span>
<% form_for Feedback.new(:annotation_id => annotation.id, :created_by_id => current_user.try(:id), :email_address => current_user.try(:email)), :url => feedback_index_path, :live_validations => true do |f| %>
<%= f.hidden_field :annotation_id %>
<%= f.hidden_field :created_by_id %>
<p style="margin-top: 1em">
<%= f.text_area :body, :rows => 4, :style => 'width:96%', :example_text => "Enter your explanation" %>
</p>
<p>
<% if current_user %>
<%= f.hidden_field :email_address %>
<% else %>
<%= f.text_field :email_address, :example_text => "Your email address" %>
<% end %>
<%= f.submit "Submit", :class => :button, :style => 'margin-left: .1em;' %>
</p>
<% end %>
</div>
However, putting more than one of these on a single page is problematic because Rails automatically gives each form an ID of new_feedback, and each field an ID like feedback_body (leading to name collisions)
Obviously I could add something like :id => '' to the form and all its fields, but this seems a tad repetitive. What's the best way to do this?
If you don't want to change your input names or your model structure, you can use the id option to make your form ID unique and the namespace option to make your input IDs unique:
<%= form_for Feedback.new(...),
id: "annotation_#{annotation.id}_feedback"
namespace: "annotation_#{annotation.id}" do |f| %>
That way our form ID is unique, i.e. annotation_2_feedback and this will also add a prefix, e.g. annotation_2_, to every input created through f.
Did you consider nested_attributes for rails models? Instead of having multiple new feedback forms where each is tied to an annotation, you could have multiple edit annotation forms where each annotation includes fields for a new feedback. The id's of the generated forms would include the annotations id such as edit_annotation_16.
The annotation model would have a relationship to its feedbacks and will also accept nested attributes for them.
class Annotation < ActiveRecord::Base
has_many :feedbacks
accepts_nested_attributes_for :feedbacks
end
class Feedback < ActiveRecord::Base
belongs_to :annotation
end
You could then add as many forms as you want, one for each annotation. For example, this is what I tried:
<% form_for #a do |form| %>
Lyrics: <br />
<%= form.text_field :lyrics %><br />
<% form.fields_for :feedbacks do |feedback| %>
Feedback: <br/>
<%= feedback.text_field :response %><br />
<% end %>
<%= form.submit "Submit" %>
<% end %>
<% form_for #b do |form| %>
Lyrics: <br />
<%= form.text_field :lyrics %><br />
<% form.fields_for :feedbacks do |feedback| %>
Feedback: <br/>
<%= feedback.text_field :response %><br />
<% end %>
<%= form.submit "Submit" %>
<% end %>
And the quick and dirty controller for the above edit view:
class AnnotationsController < ApplicationController
def edit
#a = Annotation.find(1)
#a.feedbacks.build
#b = Annotation.find(2)
#b.feedbacks.build
end
def update
#annotation = Annotation.find(params[:id])
#annotation.update_attributes(params[:annotation])
#annotation.save!
render :index
end
end
I had this same issue on a site I'm currently working on and went with the solution you mention at the bottom. It's not repetitive if you generate the ID programmatically and put the whole form in a partial. For example, on my site, I have multiple "entries" per page, each of which has two voting forms, one to vote up and one to vote down. The record ID for each entry is appended to the DOM ID of its vote forms to make it unique, like so (just shows the vote up button, the vote down button is similar):
<% form_for [entry, Vote.new], :html => { :id => 'new_up_vote_' + entry.id.to_s } do |f| -%>
<%= f.hidden_field :up_vote, :value => 1, :id => 'vote_up_vote_' + entry.id.to_s %>
<%= image_submit_tag('/images/icon_vote_up.png', :id => 'vote_up_vote_submit' + entry.id.to_s, :class => 'vote-button vote-up-button') %>
<% end -%>
I also had the same issue but wanted a more extensible solution than adding the ID to each field. Since we're already using the form_for ... |f| notation the trick is to change the name of the model and you get a new HTML ID prefix.
Using a variant of this method: http://www.dzone.com/snippets/create-classes-runtime (I removed the &block stuff)
I create a new model that is an exact copy of the model I want a second form for on the same page. Then use that new model to render the new form.
If the first form is using
#address = Address.new
then
create_class('AddressNew', Address)
#address_new = AddressNew.new
Your ID prefix will be 'address_new_' instead of 'address_' for the second form of the same model. When you read the form params you can create an Address model to put the values into.
For those stumbling here, looking for the solution for Rails 3.2 app, look at this question instead:
Rails: Using form_for multiple times (DOM ids)