I've got a user model which contains a field called, lets say, text1. In the users/new form I want to have 4 individual text boxes for this text1. In my model I will take values from these boxes and concatenate them together as comma separated values and store them in the DB. To give you an understanding, this is what I want.
<input type="text" name="user[text1][]" />
<input type="text" name="user[text1][]" />
<input type="text" name="user[text1][]" />
<input type="text" name="user[text1][]" />
How do I get this using form_for helper method? For now please don't worry yourself about the accessor method in the model, that is all taken care of. Thanks a ton.
Add few virtual attributes to your User model
class User < ActiveRecord::Base
attr_accessor :text1_part1
attr_accessor :text1_part2
attr_accessor :text1_part3
attr_accessor :text1_part4
def before_validation
self.text1 = "#{self.text1_part1}#{self.text1_part2}#{self.text1_part3}#{self.text1_part4}"
end
# make sure you fill the correct values for
# the text parts for an existing record.
# This can be done by implementing `after_find` or overriding
# `text1=`.
end
In your view code use the new attributes instead of text1
<% form_for(:user) do |f| %>
#some code
<%= f.text_field :text1_part1>
<%= f.text_field :text1_part2>
<%= f.text_field :text1_part3>
<%= f.text_field :text1_part4>
#some code
<%= f.submit "Save">
<% end %>
The previous answer gives a solution, so I am just providing some background as to why what you are asking for does not work they way you might hope.
You can indeed create a form with multiple input fields of the same name, and that data will be posted. However, when rails receives the post it automatically parameterizes the post data and/or url parameters. It essentially splits on the & and assigns the key/value pairs to the params hash. The outcome of this is that params[:user][:text1] (from your example) will have the value of the last instance of the user[text1] it encountered, since it is simply a value assignment to an existing key. You might want to dig into ActiveRecord multiparameter assignments to get an idea of how datetime attributes work, since they are similar to your use-case.
I am working on something similar and it sounds like maybe serialization is what you are looking for. Unfortunately I don't have my issues solved yet, so I can't provide anything more concrete.
Related
I think what I would like to achieve is rather straightforward. I would like to have a form with 5 fields, and a person can create any number of objects from that form (i.e. If you fill in only 1 field, you create 1 object, if you field 2 fields, you create 2 objects etc...)
Interestingly, nothing much comes up when I attempt to google this topic. The only "proper" example I came across was this. Which leads me to where I'm currently at (cause it doesn't work).
Here's my foo_controller.rb
def new
#foo = []
5.times do
#foo << Foo.new
end
end
def create
if params.has_key?("foos")
Foo.create(foo_params(params["foos"]))
else
params["foos"].each do |f|
Foo.create(foo_params(f))
end
end
end
private
def foo_params(my_params)
my_params.permit(:item_one, :item_two)
end
And here's the new.html.erb
<%= form_tag foos_path do %>
<% #foo.each do |f| %>
<% fields_for "foos[]", f do |ff| %>
#all the form stuff, labels, buttons etc
<% end %>
<% end %>
<% end %>
Now before I even hit submit, I already "know" that there's one problem. My form fields all have the same ID, which means even "if" I could submit without any problems, only the last entry would end up hitting the database. So...
Problem #1
- How do I have unique form IDs?
The next issue I face is when I hit submit, I get this error Rack::QueryParser::ParameterTypeError (expected Array (got Rack::QueryParser::Params), so basically the params is not receiving the right thing. And that's my next problem...
Problem 2
- What's the proper way to be passing an Array of params?
But that is all assuming that THIS guide's solution works. I followed pretty much every step but came up empty. So i guess my big question/problem is:
How do i create One or Multiple objects of a single model?
Looks like you are headed in the right direction.
Problem 1: The IDs
You don't need IDs if you are creating new records. If you want to be able to edit the records with the same form it may be better to generate IDs, but that is more of a decision on how you want to handle create vs. update.
The way this works is Rails looks at the Name of the form elements. If you have empty brackets in the name Rails will render the params into an array:
<input type="text" name="foo[bar][][baz]" value="one" />
<input type="text" name="foo[bar][][baz]" value="two" />
<input type="text" name="foo[bar][][baz]" value="three" />
params => { foo: { bar: ["one", "two", "three"]}}
But, if you have IDs, Rails will render params into a hash:
<input type="text" name="foo[bar][1][baz]" value="one" />
<input type="text" name="foo[bar][2][baz]" value="two" />
<input type="text" name="foo[bar][3][baz]" value="three" />
params => { foo: { bar: {1: "one", 2: "two", 3: "three"}}}
Problem 3: Creating the models
Now the trick is how you handle the creation of the models. If you want to use nested attributes you can do that by setting up #accepts_nested_attributes_for and nesting everything under bar_attributes. This will let you just pass the params hash to the model and have it work some magic.
Alternatively you might want to iterate over the params hash and make each model right in your controller.
Or create a form object to contain all that logic. Up to you.
I would start with the manual iterate-over-params approach and get things working. Then you can refactor and clean things up.
This might look something like this in your controller:
array_of_bars = foo_params[:bar].map do |bar_params|
Bar.new(baz: bar_params[:baz])
end
Take a look at RailCast's Dynamic Forms and Nested Forms for more details. I revisit those guides numerous times over the last few years and have been happy with the resulting code every time.
Nested forms is always tricky but there are gems that can do that for you. One of my personal favourites would be this: https://github.com/nathanvda/cocoon
Here's a tutorial using the gem https://www.sitepoint.com/better-nested-attributes-in-rails-with-the-cocoon-gem/
I've been dealing with the same error Rack::QueryParser::ParameterTypeError (expected Array (got Rack::QueryParser::Params). This answer helped me to fix it.
The error is caused by the fact that the records with an ID render params into a hash. The solution: you don't need the fields_for. Removing the fields_for block and just using form tag helpers instead should work, such as:
text_field_tag "foos[][name]", f["name"]
With this approach, there will be no IDs that mess with your params. If you submit and inspect the params, you will see a nice array of hashes.
I am creating a RoR application. So i want to create a web form with select drop down type. I found the following code in a project. Here is a form:
<div class="field">
<%= f.label :key_words %><br>
<%= f.collection_select(:skill_list, User::all_tags, :name, :name, {},{multiple: true}) %>
</div>
I create in User controller class :
class UsersController < ApplicationController
...
def all_tags
#tags = Tag.all
end
end
Here is database :
1. I don't understand what is User::all_tags ? Ok understood
2. I cannot see tags in my select drop down. What is missing ?
You need to understand the context first, and then need to understand it.
You are passing: User::all_tags, :name, :name
User::all_tags returns you most probably an array/hash, and this array will be utilized to populate collection_select, but a general option tag has two things: 1) value that will exactly be sent to the server, and a piece of text that will be shown, but won't necessarily be sent to the server.
The first argument after User::all_tags determines what to have for value, and second argument decides what to have for that apparent text.
<option value="First argument goes here ">"Second argument">/option> # this isn't valid code
It seems to me all_tags a method that placed in user class and have an array or hash containing the values
you can call that method like User.all_tags as well
I am creating a form in Rails by hand, ie:
<input type="number" name="funds_application[product_revenues_attributes][1][amount]">
This works fine when submitting data, but when I am editing a record the fields do not auto populate. Does anyone know how rails does this? Do I need to use a <%= text_field_tag %> to get that part of it working.
This is a small part of a larger form where I am using Simple_form, and the rest works as expected. I find complex forms sort of mind melting in rails, what does it want?
Do you have to do it by hand? Can you still use form helpers?
Anyways, the way Rails does it is that you always give the form an instance of the model you are working with, and call the getter on its attributes. For a new instance, they'll be blank. For a saved instance, they'll have values. For instance, if you had a User model with a login and name attribute, you could do #user = User.new in your controller, and in your form do (using helper tags):
<%= text_field_tag "login", #user.login %>
<%= text_field_tag "name", #user.name %>
And if you had an actual user (#user = User.first), you could still use it with that view.
So no, you don't have to use the form tags, because the underlying principle is always giving an instance of the model you are working with, and deciding what defaults to use if an attribute is nil/blank.
So if you always had an object to work with, and yet still wanted to do it manually, you could type:
<input type="number" name="funds_application[product_revenues_attributes][1][amount]" value=#my_object.amount>
Or whatever the field really is. That way, it gets some default value, but if the object already has something for that attribute, it will output it.
If you're building the form yourself, you can populate the fields yourself from the object passed in, with code along these lines:
<input type="number" name="funds_application[product_revenues_attributes][1][amount]" value="<%= #model.value %>">
The most railsy way to do it is with the form_for construct, though. The guide on this is pretty good.
I have a variable that I want to pass with a form in rails.
The variable is not convenient because it is not part of the model. I really don't want to create a column in the DB just so I can pass the variable.
When I try to pass the variable using something innocuous like:
<input type="hidden" id="blah" value=<%= "#{#blah}" %> />
The hidden field turns up correctly in the source, but doesn't pass with the params hash. When I code it properly with something like id="review_blah" rails assumes it's part of the review model and gets angry because it isn't part of the model.
Isn't there some workaround?? Some way to pass a variable easily for these circumstances, without rebuilding your model?
Thanks
You need to define an attribute_accessor for the virtual attribute and you need to add it to the attr_accessible list so it can be used in form. Like this:
attr_accessor :blah
This makes the blah and blah= methods available which you can use to deal with the incoming data.
def blah=(data)
#do sth. with the submited data here
end
In this case you could access the data entered in the "blah" field as the local variable "data".
Then you need to make it a part of the regular form:
<%= f.hidden_field :blah, :value => <Your value here> %>
So the last thing you need to do is adding the field to the attr_accessible list.
attr_accessible :blah,....
Otherwise the request would fail because of mass assignment protection.
In model
attr_accessible :blah
Though it's very ugly you can do something like following
<input name='x' type="hidden" id="blah" value=<%= "#{#blah}" %> />
in your controller
#model.save
Model.update_all({:my_attribute => params['x']}, {:id => #model.id})
You need to set the name of the input parameter I think. otherwise it will not be contained in Rails params hash.
<input type="hidden" id="blah" value=<%= "#{#blah}" %> name="blah"/>
class ModelName
attr_accessor :blah
end
#model = ModelName.new
#model.blah = "blah blah"
#model.blah #will return "blah blah"
You don't need to have a column in your table. Check here
This way you can have
=form_for #model do |f|
=f.hidden_field :blah
This will pass blah along the object in the params hash.
Thanks to help from people earlier, I am getting a hang of how to serialize a nested hash into a single column. While I was able to generate the form fields at multiple levels and get values of the fields back in to a string, I am unable to retain the different levels for the hash.
My hash looks like the following:
My code looks like:
<% categoryvalue.each do |categoryproperty, categorypropertyvalue| %>
<div>
<%= f.fields_for :categories, categoryproperty do |categoryattribs| %>
<%= categoryattribs.label categoryproperty %> <br/>
<%= categoryattribs.text_field categoryproperty, :value => categorypropertyvalue %> <br/>
<% end %>
</div>
<% end %>
The final hash string in my example takes data for two categories and must look similar to the following string when it gets assigned to :categories should look like the following:
{"0" => {"Active"=>"yes", "totalproducts"=>"100", "segment"=>"Premium"}, "1" => "Active"=>"yes", "totalproducts"=>"190"}}
However, the string is coming in the following form:
{"Active"=>"yes", "totalproducts"=>"100", "segment"=>"Premium", "Active"=>"yes", "totalproducts"=>"190"}
Is there a way to differentiate the attributes of one category from another and have two separate hashes within the main hash? Right now everything gets flattened out to a single level. This is evident in how the id and names for input fields are generated. See the sample below:
<input id="product_categories_Active" name="product[categories][Active]" size="30" type="text" value="%" />
<input id="product_categories_Active" name="product[categories][Active]" size="30" type="text" value="lbs" />
What I actually want is product[categories][0][Active] and product[categories][1][Active].
Any suggestions on how to approach this?
Just in case someone stumbles on this question having a similar problem, I wanted to share the final solution.
You have to use text_field_tag, select_tag etc. instead of the fields_for helper. Use a loop (.each_with_index do |key, index|) to iterate through your hash. Then basically generate a fully qualified name for each field in the format "product_categories_0_active".
I will try to write a tutorial or blog on this once I am out of the woods on my project but I think most people will figure it out from here.
Hope it helps.