Rails 7 collection_check_boxes not displaying checked items in edit view - ruby-on-rails

Pursuant to this thread, I've tried to get my code to display which items are already checked when you display an edit view. All other fields are pre-populated.
Here's how I have it currently:
%fieldset.border.border-dark.p-2.mb-4
%legend Pronouns
.row.mb-4
= f.collection_check_boxes :pronouns, pronoun_list, :itself, :itself, {include_hidden: false} do |b|
.col-md-4.d-grid.d-block.mb-2
= b.check_box(class: "btn-check", checked: #member.pronouns.split(",").map(&:itself))
= b.label(:"data-value" => b.value, class: "btn btn-outline-dark text-start btn-lg")
.form-group.mb-4
= f.label :pronouns_other, "Other Pronouns"
= f.text_field :pronouns_other, class: "form-control border border-dark"
I need to call include_hidden: false as otherwise the array contains a single blank item as the first in the array.
And here's the helper method it refers to:
def pronoun_list
[
"He/Him/His",
"She/Her/Hers",
"They/Them/Their",
"Zie/Zim/Zir",
"Sie/Sie/Hir",
"Ey/Em/Eir",
"Ve/Ver/Vis",
"Tey/Ter/Tem",
"E/Em/Eir",
"Prefer not to disclose"
]
end
The thing that's different from the other thread is that I'm using a helper method to call the collection of items from while the other thread is calling a collection of objects from the database. So I'm not sure how I'm supposed to get the id's in the first place.
#member.pronouns is a string which contains an Array (I'm using PostgresQL), so first I need to convert it to an array I assume as I read somewhere else, before I can call map on it. but while .map(&:itself) doesn't return any errors, the items that are listed in the pronouns string aren't being checked when the edit view is rendered.
Any ideas?

After some hours of wrangling with the code, and digging around for other examples, I finally got Rails to work properly.
%fieldset.border.border-dark.p-2.mb-4
%legend Pronouns
.row.mb-4
- pronoun_list.each do |pronoun|
.col-md-4.d-grid.d-block.mb-2
= f.check_box :pronouns, { multiple: true, checked: #member.pronouns.include?(pronoun), class: "btn-check" }, pronoun, false
= f.label :pronouns, pronoun, value: pronoun, class: "btn btn-outline-dark btn-lg text-start"
Explanation:
I have yet to get it to work with a collection_check_boxes; however I did get it to work with a each method and then using multiple: true on each check box.
I also set the checked_value to the pronoun and set the unchecked_value field to false. if I don't set it to false and have multiple selected, I get a bunch of zeros in the array. with false set, I only get the ones I selected in the array.

I just went through this a bit, the way collection checkboxes works is it will call first and last on object in the collection you pass it. This is so you can have a different label shown to the user and pass a different value to the server. So in your case if you created the collection like
pronoun_list = [ [Him, Him], [Her, Her] ] #contrived example
Then remove the :itself, :itself from the input. The collection checkboxes helper will then call .first and .last on each of the nested arrays using Him, Her for both the label for the checkbox and the value for the checkbox sent to the server when submitting the form
When using a collection that comes from the db to begin with like User.all you can tell collection checkboxes what to use for the value and what to use for the label eg :id and :first_name where you have :itself, :itself

Related

Field gets radio button's item id instead of its value

I have snippet:
<%= f.input :purpose, as: :radio_buttons, collection: category.subcategories,
wrapper: :vertical_collection_inline %>
which lines values of category.subcategories horizontally how I want
The problem is, when I select either of option, it assigns that option's ID, but not its value.
How should I refactor the code?
Using IDs has advantages as warned in the comments, however what you’re trying to do should work with either:
category.subcategories.collect(:&values)
Where values is the name of the field which hols “Rent” etc.
The more railsy way to do this is with collection_radio_buttons, like this:
f.collection_radio_buttons(:purpose, category.subcategories, :value, :value)
Again where “value” is the field name.

having trouble understanding :multiple => true in a checkbox

I am very new to Ruby on Rails, I have inherited control of a Ruby/Rails web based database(created by another) for generating and tracking engineering functions. I use Aptana IDE.
My issue is that I am trying to use a form helper to select multiple values for our "product lines". I use 10 check boxes, and I get the correct output of one value if I don't use ":multiple => true".
See output image:
works as intended
<%= f.check_box :product_line, {:class => "field"}, "A9", false %> A9
(10 times with different values where "A9" is, so there are ten checkbox's total, image shows "A7" check box returned)
By simply adding the :multiple => true, the output changes as seen in the image below:
see output image:
returns too much
<%= f.check_box :product_line, {:class => "field", :multiple => true}, "A9", false %> A9
Why are all of those "-" added before the output only if the multiple selection is made? I want it to return only comma separated values of the checkbox, i.e. " A9, A7"
PRODUCT_LINES = [ "A9", "A7", "AG", "AF", "S3", "Legacy", "K/Kpro", "EMW", "HD", "Non-Metallic" ]
other ideas:
Can I loop through an array of :product_line to get what im looking for instead?
I was also playing with the multiple selection in a drop down menu but could never select a second option before the drop down returned and only selected my first selection.
Why are all of those "-" added before the output only if multiple
selection is made?
From the docs (read the Gotcha) when multiple is set to true all selections will be stored in an array of product_lines but un-selected check_boxes will also be reserve as an empty strings in the sent pararms so for the result you are seeing product_lines would be something like:
product_lines = ["", "", "", "A9", "A7"]
Can I loop through an array of :product_line to get what im looking
for instead?
Well, actually this takes us right to the problem in using multiple: true in your code.
The process explained in (1) above is how the check_box_tag was designed, the problem you are seeing is in presentation probably in your ECN#show. Without reviewing code there i am only guessing but usually it's something like:
<%= #ecn.product_lines.join('-') %>
which should be
<%= #ecn.product_lines.reject(&:blank?).join(',') %>
where: reject(&:blank?) to get rid of empty strings then joining with commas as you need
Hope this helps!

Ruby on Rails: putting class with submit_tag

I was wondering why we have to add a nil when putting :class => "class_name"
<%= submit_tag nil, :class => "class_name" %>
but for this:
<%= f.submit class: "class-Name" %>
I don't need to add the nil
Thanks
<%= submit_tag("Update", :id=>"button", :class=>"Test", :name=>"submit") %>
First parameter is required and it would be value and they any parameter you want to specify, can be done in a hash like :key=>"value".
A look to the way that submit_tag method was implemented clearly answers your question.
def submit_tag(value = "Save changes", options = {})
options = options.stringify_keys
if disable_with = options.delete("disable_with")
options["data-disable-with"] = disable_with
end
if confirm = options.delete("confirm")
options["data-confirm"] = confirm
end
tag :input, { "type" => "submit", "name" => "commit", "value" => value }.update(options)
end
It takes two arguments, the first is value which by default is "Save changes" and the second is a Hash of options. If you don't pass nil then it will assume that that's the value you want for the input.
Because they are two different methods...
The "submit" method doesn't take a caption because it can infer one from the form that the method is called on, and what object was used to build the form.
The "submit_tag" method is not called on a form object. It is used for more customized form building (more separated from your activerecord model, for example) and so the code can't infer a caption and must get a value as the first argument. All the "formelement_tag" methods (documented here, for example) are like this and can infer less based on your data model.
Obvious answer is that submit_tag and submit are simply different form helper methods that takes different arguments.
The _tag series of methods usually require a name parameter (otherwise they'd be fairly useless tags, so it's always the first argument instead of part of the hash. Because the submit helper is called as part of the form, Rails can assume the field's name property and can then make the options hash the first argument.

How to make a check_box checked in rails?

I made checkboxes using the following rails form helper:
<%= check_box("tag", tag.id) %>
However, I need to make some of them checked by default. The rails documentation doesn't specify how to do this. Is there a way? How?
Here's how to do it as of rails 4, I didn't check older documentation.
<%= check_box("tag", tag.id, {checked: true}) %>
This will make the checkbox checked. Of course instead of true you will put in some logic which determines if each one is checked.
If you need the check_box to be checked on new, and correctly filled on edit you can do:
<%= f.check_box :subscribe, checked: #event.new_record? || f.object.subscribe? %>
As I mentioned here
The rails docs do say how to have it checked and it depends on the object. If you don't have an instance object to use with check_box, then your best option is to use the check_box_tag as mentioned. If you do, read on.
Here's the link to the docs on the check_box helper. Basically how this works is that you have to have an instance variable defined. That instance variable must have a method that returns an integer or a boolean. From the docs:
This object must be an instance object (#object) and not a local
object. It’s intended that method returns an integer and if that
integer is above zero, then the checkbox is checked.
For example, let's assume you have a #tag instance in your view which has an enabled method. The following snippet would cause the checkbox to be checked when enabled is true on the #tag object and unchecked when it is false. To have it enabled by default, set the enabled attribute to true in your controller. The last two variables are the values that you want to submit with the form when the check box is checked and unchecked.
<%= check_box "tag", "enabled", {}, "1", "0" %>
A lot of times, you'll see the check_box helper used with a form builder. So if form_for was used for the #tag instance, you would more than likely use this snippet:
<%= f.check_box :enabled %>
No need of writing checked: true for rails >= 4.0
Simply write
<%= check_box_tag "name", value, true %> # true or false
The check_box_tag instead of check_box has a way to set that it's been checked.
Using check_box_tag you can set it to true so that it's already checked. More info here: http://api.rubyonrails.org/classes/ActionView/Helpers/FormTagHelper.html#method-i-check_box_tag
This answer is only related to f.check_box
f.check_box can take up to 4 arguments:
input name
hash containing options such as html class, id, etc
which value represents "checked" (defaults to "1")
which value represents "unchecked" (default to "0")
So if your boolean field is stored as either 1 or 0 (default for Rails) then everything is fine and well. Rails will recognize whether the field is checked or not.
However, if you're using a different data storage or a framework which is storing booleans in another format, you need to specify which values correspond to checked and unchecked.
Here's an example:
<%= f.check_box :accept_privacy_policy, { class: 'my-class' }, "true", "false" %>
References
https://api.rubyonrails.org/v6.0.0/classes/ActionView/Helpers/FormHelper.html#method-i-check_box
Problem with all of these solutions is that it doesn't play well with the params hash on resubmits, so at the moment I'm using something like this,
# ApplicationHelper
def resolve_boolean_parameter resource, attribute, options = {}
default = options.delete(:default)
return default unless params[:utf8]
return params[resource][attribute] == "1"
end
and then in the view:
<div><%= f.label :accepts_newsletter, "Receive Newsletters" %>
<%= f.check_box :accepts_newsletter, :checked => resolve_boolean_parameter(:user, :accepts_newsletter, default: true) %>
</div>
New Function placed in your Helper
def check_if_true(item)
ActiveModel::Type::Boolean.new.cast(item)
end
In your View
<%= check_box("test", "active", {checked: check_if_true(#test.active) , :multiple => true, :style => "margin-left: 16px;"}, "true", "false") %>
This is how I did this now in 2020.
<%= form.check_box :with_cost, checked: true, class: '', type: 'checkbox', id: 'cost' %>
With the checked: true attribute.
Hope this helps.
If you are using the form helper in Rails 5.1 + to build your checkbox you have to pass in a hash the values with specific order.
= form.check_box :open_after, { class: 'form-control mt-1', data: {}, checked: true }, true, false
As you can see, inside the hash, first is class, next data and last checked. This works for me with Rails 6.0

Array as Parameter from Rails Select Helper

I'm working on a legacy project that is using acts_as_taggable_on which expects tags to come in arrays. I have a select box allowing users to select a tag on a Course in a field called categories. The only way mass assignment create will work is if params looks like this params = {:course => {:categories => ['Presentation']}}. I've currently a view with this helper:
<%= f.select 'categories', ['Presentation' , 'Round Table' , 'Demo', 'Hands-on'] %>
Which will give me a parameter like params = {:course => {:categories => 'Presentation'}}. This doesn't work since Acts as tag gable apparently can't handle being passed anything other than a collection.
I've tried changing categories to categories[] but then I get this error:
undefined method `categories[]' for #<Course:0x007f9d95c5b810>
Does anyone know the correct way to format my select tag to return an array to the controller? I'm using Rails 3.2.3
I didn't work with acts_as_taggable_on, but maybe this simple hack will be suitable for you? You should put it before mass-assignment.
category = params[:course][:categories]
params[:course][:categories] = [category]
If you are only going to allow the selection of ONE tag, you could do:
<%= f.select 'categories', [['Presentation'] , ['Round Table'] , ['Demo'], ['Hands-on']] %>
Each one item array will have first for the display value, and last for the return value, which in this case will both return the same thing, as the first element of the array is the same as the last element when the array as one element.
Seems like select doesn't give you that option.
If I understand correctly, one option might be to use a select_tag instead and just be explicit about where you want the selection in the params:
<%= select_tag 'course[categories][]', options_for_select(['Presentation' , 'Round Table' , 'Demo', 'Hands-on']) %>
That ought to get your params the way you need them.
Here's what I'm using for one of my projects:
<% options = { include_blank: true } %>
<% html_options = { required: true, name: "#{f.object_name}[#{resource.id}][days][]" } %>
<%= f.select :days, DAYS, options, html_options %>
Without html_options[:name], Rails handles the name of the select tag and spits out something like
service[service_add_ons_attributes][11][days]
but I need
service[service_add_ons_attributes][11][days][]
So I override it.
Hope that helps.

Resources