How to get a hash in a Rails form - ruby-on-rails

I have a Rails model with 2 fields: 'name' and 'bucket'. Name is a string and bucket is a hash of the form: bucket: {red: 1, green: 2, ...}.
I created a rails form to populate these fields but while name works properly, the rest don't even appear as values.
This is the point that it doesn't work, when I am trying to bind a color to a value provided by the user, with default value 0.
<% #board.fetch_all_colors.each do |color| %>
<%= form.label color.to_sym, "#{color}:" %>
<div class="input">
<%= form.text_field :bucket, value: 0.0 %>
</div>
<% end %>
The thing is that when I debug and print params, I only find an empty bucket hash. One problem seems to be that because of the each loop, I only get the last input text.
Edit: I found a workaround that takes me half there. Instead of a text_field, I am using the following:
text_field_tag "bucket[]"
The above uses my input normally and puts it in an array. The problem with that is that since the input is dynamic (and could have labels come and go, I can't guarantee for the position of the array (e.g. that 3rd position is always 'red'). Can I use something like the above but with a hash?

While JSON-like objects are supported by rails DB drivers, the rest of the rails stack doesn't have seamless API's for specifying multi-value column attributes.
You'll need to dynamically build your hash in the controller from custom-defined attributes which don't have a 1:1 mapping to your model.
Alternatively in a more Rails-like fashion, you could use a has_many relation, storing your buckets as a separate model in the database which is joined to your Board model. accepts_nested_attributes_for would help you do this.

Related

How to tell options_for_select field where to save selected option value?

I'm returning to Rails after almost 5 years away and building out a personal project. In my _form.html.erb file, I'm trying to use a field, but the data never gets saved where I think it will be.
<select>
<%= options_for_select([['black'], ['blue'], ['red']], :selected => :color) %>
</select>
In my index, when I try to use model.color I get nothing returned. I'm sure its something basic I'm not getting, but for some reason Google searches and example code doesn't look like exactly like what I got. I'm not sure what option to pass to tell the form where to save the selected value.
You need to give the select tag a name (e.g. <select name="foo"> populates params[:foo], <select name="foo[bar]"> populates params[:foo][:bar]; alternatively use the select_tag/select form helper methods – see https://api.rubyonrails.org/classes/ActionView/Helpers/FormTagHelper.html#method-i-select_tag and https://api.rubyonrails.org/classes/ActionView/Helpers/FormBuilder.html#method-i-select).
Be aware: If you haven't used Rails for several years, chances are you don't know about strong parameters which you need to use if you want to do direct assignment (e.g. User.new(params[:user]) doesn't work as it used to in older Rails versions – you need to use strong parameters here). Details: https://edgeguides.rubyonrails.org/action_controller_overview.html#strong-parameters.
You need to assign the input a name in order to do anything meaningful with it - This is not Rails specific. Its universally true for web development. Form data in HTTP is just a bunch of key value pairs and the name attribute sets the key.
In Rails you should use the form builders and input helpers unless you have a very good reason not to - it is after driven by convention over configuration. They will handle assigning the name attribute for you:
<%= form_with(model: #thing) do |f| %>
<%= f.select :color, [['Black','black'], ['Blue','blue'], ['Red', 'red']] %>
# ...
<% end %>
This renders something like:
<select name="things[color]">
<option value="black">Black</option>
# ...
</select>
And would give you the following params hash:
{
thing: {
color: 'black'
}
}
They also provide data bindings between your model and the form so that the inputs are not reset when the user submits an invalid form.
I would really start by reading the rails guides as you have a lot to catch up on.

How to show text instead of integers?

I have a form with a pull down list of Races (asian, caucasian, African/black, etc).
These races are represented by my Race model. I can create a record successfully, however, when I try to view the record the corresponding integer of the race is displayed and not the text.
How do I get the text associated instead of the integer id?
Thanks.
Provided you supply a minimum set of data for us to assist, I risk an answer as generically as I can.
If you are using a form_for builder
<%= f.collection_select :race_id, Race.all, :id, :name %>
or whatever you call your fields
If you are using a form_tag
<%= select_tag :race_id, options_from_collection_for_select(Race.all, "id", "name") %>
These are as I said pretty generic answers, you can build further on them. Check this link for more:
http://api.rubyonrails.org/classes/ActionView/Helpers/FormBuilder.html
You are seeing the value of the selected option which is the index of the array I imagine. You can use that index to get the text value from your races array. Without seeing more code that is all I can provide.

Rails nested form with a "select" form element giving me "undefined method `merge' for #<ActiveSupport::SafeBuffer:0xb50d34b8>"

Background: My goal is for a view to display a list of "condition" has_many objects, which are themselves STI subclasses of a StateDescription. I want a user to be able to pick what type of state description they are from a drop down menu (which will conditionally display a different type of form, eventually)
Inside of my main forms, I am doing a nested form like this:
<%= f.fields_for :conditions do |e| %>
<li>
<%= e.select(:type, StateDescription.subclasses.collect{|x| x.to_s}, options_for_select(StateDescription.subclassSelectForms)) %>
<br>
<%= e.label :title %>
<%= e.text_field :title %>
</li>
<% end %>
This works just fine with the text field at the bottom there (I can change values and save them, etc). But when I try to do a select statement, it explodes.
Specifically, if I don't use e.select and just do:
<%= select(:type, StateDescription.subclasses.collect{|x| x.to_s}, options_for_select(StateDescription.subclassSelectForms)) %>
it renders just fine, but doesn't actually change any values as it is not associated with a model.
If I get rid of trying to have it display a value different than it submits and just do
<%= e.select(:type, StateDescription.subclasses.collect{|x| x.to_s}) %>
Then it works just fine(I can submit, the value is saved to the database, I can retrieve it, etc).
I can LEAVE it like this, but I had wanted to have my more human readable string display instead of a rails class thing (which might look like gibberish to a non-programmer end user).
So, my question is: Why do options_for_select break when nested and associated with a form, but dont' seem to when not associated with a form? Is it just a known bug? Am I doing something wrong?
Edit:
.subclasses is a standard rails calls which returns a list of subclasses for an object.
.subclassSelect forms goes through each subclass, and turns it into a hash of the form:
{subclass.to_s => subclass.human_readable_string} and compiles all of them into an array.
I know this array of hashes works, because if I do NOT put "e" on the front, it displays correctly and works "right" for what I have done (i.e. the subclass is returned correctly based on which human readable string I select from the drop down menu, but since it's not actually associated with any form, it doesn't get set in the controller).

How to convert an integer column in the model to be used as a string in the view

For database columns that are integers that "represent" strings, what is the best way to show the string value in the view?
For example, if I collect "payment_method" values as integers in my form as follows:
<%= f.select :payment_method, { "Visa" => "1", "Mastercard" => "2", "Amex" => "3"} %>
How can I show the saved integer as a string in my view files? What can I add to my model, so that
<%= #relevantvariable.payment_method %>
or something similar returns string values like "Visa", "Mastercard" or "Amex" instead of their respective integer values?
Thanks much for handling such a basic question!
Either don't use an integer value, and store the payment method directly as a string in the db, or create a PaymentMethod model.
With the association set up, you'd be able to refer to the name of the payment method as so:
<%= #relevantvariable.payment_method.name %>
Don't try to manually handle lists of names / ids - that will quickly get unmanageable.
Edit: after reading your comment, if you went with the first option and stored a string in the table, definitely don't allow the user to type the value directly, maintain a list on the model (or similar), that you seed your dropdown from, that way you're still constraining the possible values. You could even add a custom validator if you want to be certain what you putting in the database.
I'd stick with cheeseweasel solution but you can do one thing to show that on your view...
<% if #relevantvariable.payment_method == 1 %>
<%= "Visa" %>
<% else %>
<%= "Mastercard" %>
You probably would want to use a switch/case instead but you got the idea
As I said I think you should stick with cheeseweasel solution since there are many problems with this approach... it's your call
So you have your payment methods in a separate table payment_methods and the owner ( say user) contains a has_one relationship with it
class User < AR::Base
has_one :payment_method
end
Then show the payment method name just like
<%=
#user.payment_method.name #or whatever you have.
%>
However, while you are loading the #user data, you can perform a eager loading by :include. Like
User.find(user_id, :include => :payment_method)
This will load the PaymentMethod data earlier even for multiple users with single query.

rails form using database

i have a rails form ive made in which the form fields are extracted from the database. i did it like this because for different products, there are different form fields. i could have made one big order form to do it, and if the product field didnt apply to the product it would be left blank, but it seemed like making the fields being called from a database made more sense because there are 30-40 fields per order. anyways the error in which im running into is when im extracting the row field_type, it prints out the literal value instead of putting it in rails. heres what it looks like:
<% #form_field.each do |field| %>
<p>
<%= "f.#{field.field_type}" %> #this prints out f.text_field
</p>
<% end %>
Instead of printing out f.text_field, i would like it to actually make a text field. I tried using raw but no look seeing as thats for html. is there a way to do this in rails?
You'd need to build up the string and send it to f, like f.send(field.field_type) (untested) along with any arguments needed for that particular form field type.

Resources