How to submit an array of integers in a Rails form? - ruby-on-rails

I would like to know if there is a way to have some sort of input field in a Rails form, so that if the user enters 1, 2, 3, then params[:model][:attribute] returns [1, 2, 3] or at least ['1', '2', '3'], but not ['1, 2, 3'].
Background:
I have a model Foo, which has an attribute bar_ids. The datatype of this attribute in the PostgresQL database is Array. I've tried several things:
if f.text_field :bar_ids then params[:foo][:bar_ids] returns '1, 2, 3'
if f.text_field_tag 'foo[bar_ids][]' then params[:foo][:bar_ids] returns ['1, 2, 3']
if f.number_field :bar_ids then params[:foo][:bar_ids] returns '1' if I input only 1 and the form does not allow to input multiple numbers
So, again my question - is there a way to construct my form in such a way, so that Rails automatically parses the input to the respective datatype, in my case - an array of integers?

People usually edit the params manually before updating the model:
params[:model][:attribute] = params[:model:][:attribute].split(',')
# ...
Model.update_attributes(params[:model])
It is usually done in controller action or in before_action.

Related

How can remove Null value from f.select in rails?

I have multiple dropdown when I select items. It also creates null value How can I remove null values from this array?
= f.select(:doc, file.all.collect {|a| [a.name, a.id]}, {}, id: "id-select2", class: "form-control", :multiple => true).
You have two problems there
Your collect is returning some nil elements(As Joseph said), in this case the name attribute is what it can be nil, so you can check for that on the collect
Solution(compact) [UPDATE]
f.select(:doc, file.all.collect {|a| [a.name, a.id] if a.name, include_hidden: false }.compact, {}, id: "id-select2", class: "form-control", :multiple => true)
Specify the include_blank option https://api.rubyonrails.org/classes/ActionView/Helpers/FormBuilder.html#method-i-select
f.select(:doc, file.all.collect {|a| [a.name, a.id] if a.name }.compact, { include_blank: false, include_hidden: false }, id: "id-select2", class: "form-control", :multiple => true)
According to the docs for select helper I have found this gotcha
The HTML specification says when multiple parameter passed to select
and all options got deselected web browsers do not send any value to
server. Unfortunately this introduces a gotcha: if an User model has
many roles and have role_ids accessor, and in the form that edits
roles of the user the user deselects all roles from role_ids multiple
select box, no role_ids parameter is sent. So, any mass-assignment
idiom like To prevent this the helper generates an auxiliary hidden
field before every multiple select. The hidden field has the same name
as multiple select and blank value.
Note: The client either sends only the hidden field (representing the
deselected multiple select box), or both fields. This means that the
resulting array always contains a blank string.
In case if you don't want the helper to generate this hidden field you
can specify include_hidden: false option.
So if you add the include_hidden: false option then you won't get the empty string on your multiple option when the data is sent to the controller.
You can use compact. For example:
a = [nil, 2, 3, 4, 5]
without_nil = a.compact
# [2, 3, 4, 5]
using compact! will modify the original array whereas compact returns a new array.
you can filter that in database query for example (I assume id is primary key and is not null):
file.where('name IS NOT NULL').load
compact isn't working for you because you're likely calling it on the result of the collect. If you have an array like [['a', 1], ['b', nil]], calling compact on it won't do anything because the ['b', nil] is not nil. It only contains it. So you need to avoid loading the Files where name == nil.
You'd want something like this instead:
f.select(:doc, file.where('name IS NOT NULL').collect { |f| [f.name, f.id] }, {}, id: "id-select2", class: "form-control", multiple: true)
It might be more helpful if we knew exactly what file is.

Rails 4 - key value pair

I'm trying to make an app in Rails 4.
I've recently asked these 2 questions, and taken the advice in the responses. Rails 4 - how to use enum?
I'm still struggling.
I have a form with an input selector:
<%= f.input :self_governance, as: :select, label: "Select your governance approach", collection: Preference.self_governances.to_a.map { |p| [p.to_s.humanize, p] } %>
When I save this and try it, the select menu shows:
["tier_1", 1]
What I want is to display: Tier 1
At the moment, I have a preference model with:
enum self_governance: {
tier_1: 1,
tier_2: 2,
tier_3: 3,
tier_4: 4,
tier_5: 5
}
enum autonomy: {
tier_11: 1,
tier_21: 2,
tier_31: 3,
tier_41: 4,
tier_51: 5
}
I have a preferences show view:
<%= #organisation.preference.self_governance.try(:humanize) %>
Also, when I accept the form problem (for now) and try to render the show page, I get this error:
'["tier_1", 1]' is not a valid self_governance
Can anyone see what I've done wrong?
I just want to save the number 1 in the database, but display the words 'Tier 1'.
Update your form to properly return a collection of keys and values from your enum. Preference.self_governances is a type of hash object.r Rather than call to_a, just iterate over the keys and values:
<%= f.input :self_governance, as: :select, label: "Select your governance approach", collection: Preference.self_governances.map { |key, val| [key.humanize, key] } } %>
If we look at just the output of:
Preference.self_governances.map { |key, val| [key.humanize, key] }
We get the following:
[
["Tier 1", "tier_1"],
["Tier 2", "tier_2"]
...
]
Note that the first value is what gets shown as the select label and the second value is what gets sent to your controller within the param.
EDIT:
When using an enum, you can assign either the key or value of the enum to the field.
preference.self_governance = 1 # Works
preference.self_governance = :tier_1 # Works
preference.self_governance = "tier_1" # Works
But you can't assign the value as a string:
preference.self_governance = '1'
=> "ArgumentError: '1' is not a valid self_governance" # Doesn't work, tries to look for key '1' in enum, but doesn't exist.
So make sure you pass the key of the selected enum (i.e. "tier_1") aback to your form or else you ma

Active admin: Custom check_box input

I need to show a dynamic list of values to show on an Active Admin edit screen as checkboxes, where the list comes from code (not database). I can do that pretty easily, but I can't figure out how to show some of those as being checked.
Here's a simplified example of what I'm trying to do:
names = %w(Sam Darcy Ernie)
pairs = Hash[names.zip(names)]
f.input :buddies, as: :check_boxes, collection: pairs, checked: %w(Sam)
What I was hoping for is to show the 3 checkboxes and have the "Sam" box checked. None are checked though. What can I do to control which checkboxes are checked?
I ended up with the following that functions as desired:
people = [
['Sam', 0, checked: true],
['Darcy', 0],
['Ernie', 0],
]
f.input :buddies, as: :check_boxes, collection: people
It seems that the array items beyond the first 2 are used to set attributes. So in my case, the "checked" attribute is set, resulting in the element attribute of checked="checked"
I'm still interested in knowing if there is a better way of handling this though.

Text fields from array in Rails

I'm trying to generate a set of text fields for an array using Rails 2.3. I have an array in my controller (which is not part of the model), and I'd like to make a text field for each entry. The array is like:
#ages = [1, 3, 7] # defaults
Then, I'd like to generate 3 text field in my view with the values 1, 3, and 7, and have the array filled with the user's values when submitted.
I found a bunch of stuff on Google and here, but none that seemed to work for me. I'm sure this is easy in Rails...
Rails can serialize collections, which should make this easier.
If you name your inputs like 'field[]' like this in your view:
<% #ages.each do |age| %>
<%= text_field_tag 'ages[]', age %>
<% end %>
Then you can access all 'ages' in your controller on submission:
#ages = params[:ages] # ['1', '3', '7']

Select tag, specifying the selected option (or moving an array element to index 0)

Say I'm querying a list of fruit and then collecting just the id's and name's of the fruit into #fruit.
[32, "apple"],
[8, "bannana"],
[10, "cantelope"],
[11, "grape"],
[15, "orange"],
[41, "peach"],
[22, "watermelon"]
#fruit is being used in a Select helper. "apple" being at index 0 of #fruit will be the selected value (first option) of the select. This is a made up example but by default I'll always know what "orange" is by name (not by id). I need "orange" to be the selected value of the select tag (the first option).
":prompt => 'orange'" just adds a 2nd instance of "orange" in the select. Everything I've found on Google so far seems to be about adding an extra value or blank to the list.
Since index 0 of the array always becomes the selected value (if no prompt or blank is used in the select helper), is there a way I could find the index containing the name "orange" (#fruit[x].name == 'orange'), and move it to index 0 while retaining the existing alpha sort in the rest of the list? So, the #fruit array would look like:
#fruit[0] [15, "orange"],
#fruit[1] [32, "apple"],
#fruit[2] [8, "bannana"],
#fruit[3] [10, "cantelope"],
#fruit[4] [11, "grape"],
#fruit[5] [41, "peach"],
#fruit[6] [22, "watermelon"]
The only thing I can think of right now would be to iterate through #fruit and if "orange" is found add it to a new array. Then iterate through the #fruit array again and add anything that doesn't have a name of "orange". I'm not sure how to write that out but it seems like it would do what I'm looking for. Maybe there's some easy way to do this I'm missing (specifying which index in an Array is to be the first option written)?
Thank You!
See here: http://guides.rubyonrails.org/form_helpers.html#making-select-boxes-with-ease
If you use the existing helpers, you can specify by id which option you want selected. Specifically this example:
<%= options_for_select([['Lisbon', 1], ['Madrid', 2]], 2) %>
output:
<option value="1">Lisbon</option>
<option value="2" selected="selected">Madrid</option>
You say you don't know which option is "orange" by ID. If you want to find the ID for "orange" in the fruit array this would do it, then you can use the helper:
(#fruit.detect { |i| i[1] == 'orange' } || []).first
You can use options_from_collection_for_select in the select tag for selecting a default option. For example:
<%= select_tag 'state',
options_from_collection_for_select(#fruits, 'id', 'name', 15),
:include_blank => true %>
This would select 'Orange' by default. API Docs.

Resources