Multiple column names/custom label in Rails collection_check_boxes - ruby-on-rails

I have a form_for in Rails that uses the collection_check_boxes method for an array of parts that get listed as check boxes. I want the label of the checkbox to be a composite of two methods so that each part's checkbox is labeled with it's name and it's SKU number:
my form code:
<%= collection_check_boxes(:service, :part_ids, #parts_list, :id, :sku, {}, {checked: true}) %>
this works perfectly to create a list of check boxes. But each label is only the :sku since I can only pass it one method for the label. Does anyone know of a way to create a custom label for these? I have read the api docs and it mentions using a do |b| ... end block but doesn't explain how to use it or why.

Here is the modified form code:
<%= collection_check_boxes(:service, :part_ids, #parts_list, :id, :sku, {}, {checked: true}) do |b|
b.label { b.check_box + b.object.name + " " + b.object.sku}
end %>
NOTE:
b.object.name #### Assuming that name is field name in your model
b.object.sku #### Replace with b.object.sku.to_s if sku is numeric field.

You could define a custom field in your model:
def cb_label
"#{name} #{sku}"
end
and in the collection
<%= collection_check_boxes(:service, :part_ids, #parts_list, :id, :cb_label, {}, {checked: true}) %>

Related

collection_select show multiple object attributes?

In addition to showing the challenge's name. I also want to show its deadline next to the name.
It would look like this for example:
Visit London 09/09/16
Make $1,000,000 10/15/18
Knit a Scarf 01/11/19
Instead of just this:
<%= f.collection_select :challenge_id, current_user.challenges.order(:deadline),:id,:name, include_blank: true %>
Define a method name_with_deadline in challenge.rb
def name_with_deadline
"#{name} #{deadline}"
end
and then make use of this method as label in the collection.
<%= f.collection_select :challenge_id, current_user.challenges.order(:deadline),:id, :name_with_deadline, include_blank: true %>
The name_with_deadline method will called for every object in the collection to retrieve the label text.
Hope this helps!
You can add a virtual attribute to you model like below:
def name_deadline
"#{name} #{deadline}"
end
collection_select:
<%= f.collection_select :challenge_id, current_user.challenges.order(:deadline),:id,:name_deadline, include_blank: true %>

Rails Bootstrap_form - Radio buttons not rendering label name in the show page

I have a form built with the bootstrap_form gem for ruby. within the form I have some radio buttons. the form renders perfectly with the correct labeling in the edit page of the form, however once I save the form and go to the "show" page to see the results, the inputs for the radio buttons are only showing the value of the radio button (which are numbers) and not the custom label name I have assigned. How can I make the custom label appear instead of the value in the show page?
Here is my code:
_form.html.erb:
<%= f.form_group :gender, label: { text: "Gender" } do %>
<%= f.radio_button :gender, 0, label: "Female", checked: true, inline: true %>
<%= f.radio_button :gender, 1, label: "Male", inline: true %>
<% end %>
<%= f.form_group :nationality, label: { text: "Nationality" } do %>
<%= f.radio_button :nationality, 0, label: "Kuwaiti", checked: true, inline: true %>
<%= f.radio_button :nationality, 1, label: "Non-Kuwaiti", inline: true %>
<% end %>
show.html.erb
<p><strong>Full Name:</strong> <%= #profile.name %></p>
<p><strong>Civil ID no:</strong> <%= #profile.civil %></p>
<p><strong>Gender:</strong> <%= #profile.gender %></p>
<p><strong>Nationality:</strong> <%= #profile.nationality %></p>
<p><strong>Mobile no:</strong> <%= #profile.mobile %></p>
<p><strong>Personal Email:</strong> <%= #profile.personal_email %></p>
Thanks for the help in advance!
Update:-
New form code:-
<%= f.form_group :gender, label: { text: "Gender" } do %>
<%= f.radio_button :gender, 1 %>
<%= f.label 'Female', inline: true, checked: true %>
<%= f.radio_button :gender, 0 %>
<%= f.label 'Male', inline: true %>
<% end %>
Tried this suggestion but still getting the same problem, only the the radio buttons are no longer in line and have incorrect formatting.
You need a lookup table
You have saved numeric values that correspond to the radio button. So, when you display the variable in the show.htm.erb it is showing the numeric values retrieved from the database record. This makes sense. But you need the string associated with the number, which requires a lookup table
Creating labels in the form does not create custom labels for your field. To create custom field labels, you would need to setup i18n localizations for your activerecord models, which is a good solution, but also one that will take some time and learning curve.
Creating a hash-based lookup table.
Active Hash gem
You could write your own lookup helper or use this gem which simplifies implementing hash-based lookup tables.
Example
# in app/models/person.rb
class Gender < ActiveHash::Base
self.data = [
{:id => 0, :gender => "Female"},
{:id => 1, :gender => "Male"}
]
end
From their spec
We wrote ActiveHash so that we could use simple, in-memory, ActiveRecord-like data structures that play well with Rails forms...
Build error in gem (Update)
I just checked an it seems their build has an error. Probably best to avoid for now.
Using view helpers to translate boolean values to strings (Added)
You could implement helpers your /helpers/profile_helper.rb. I call them "lookups," but that is probably not standard.
For each of the boolean you need to translate to strings create a helper
#profile_helper.rb
# Each of your booleans
def gender_to_s(value)
value ? "Male" : "Female"
end
def nationality_to_s(value)
value ? "Kuwaiti" : "Non-Kuwaiti"
end
# Generic true/false conversion
def boolean_to_s(value)
value ? "Yes" : "No"
end
Note: I would name the helpers consistently to match the field name, so in your view it is very obvious which "to_s" to invoke.
In your view
<p><strong>Gender:</strong> <%= gender_to_s(#profile.gender) %></p>
Note: If you wanted a single view helper, this could be implemented in application_helper.rb and include a passed parameter "type" as a switch control, but I think the most straightforward solution is to create a view helper for each field.
You want a separate tag for the labels. Instead of:
<%= f.radio_button :gender, 1, label: "Male", inline: true %>
You need:
<%= f.radio_button :gender, 1 %>
<%= f.label 'Male, inline: true %>
Might also be worth looking at this answer: Bootstrap 3: does form-horizontal work for radio buttons with a control-label?
It doesn't use the form label helper, but it looks like it does what you need.
OK one more approach so that you have a full set to choose from. In Rails 4.1, enum data types were implemented in ActiveRecord as described in section 2.5 of the release notes. If you are running Rails 4.1+, you can take advantage of this new feature.
Essentially, you will need to write migrations to change your boolean and other data types to enum. For instance, for the gender example, rather than having gender of 0 signify female and 1 signify male, and maintain a mapping for that, you could simply have:
enum gender: [:female, :male]
and likewise for your other attributes.
This should ultimately be easier to maintain and will help keep your application code as transparent as possible.
<label><%= form.radio_button :gender, 1, hide_label: true, inline: true%>Female</label>
<label><%= form.radio_button :gender, 0, hide_label: true, inline: true%>Male</label>
worked for me Bootstrap 4.1

Rails form dropdown using two text_method

I am creating a form has a drop down selection. I want to use two "text_method"s for the input but I am unsure how to do this. I want to include the year and name (both are two different columns in my rails model.
Here is what I have but it does not work:
<%= f.collection_select :bat_id, Bat.all, :id, :model_year, :model_name, include_blank: true %>
Here is the official documentation- http://api.rubyonrails.org/classes/ActionView/Helpers/FormOptionsHelper.html#method-i-collection_select
Use this in your view:
<%= f.collection_select :bat_id, Bat.all, :id, :model_year_and_name, include_blank: true %>
Add a method like this to your Bat model:
def model_year_and_name
"#{model_year} #{model_name}"
end

Rails Multiple Input Field in Form to One Integer Attribute in Model

I am trying to allow a user to input two different things in two different drop down menus from the same form and it will store an integer into a review table.
I want the user to be able to select model_name in one drop down and manufacturer in another drop down. The result will store a bat_id integer into the form. (Telling you which bat the user is selecting)
I have seen a couple questions about date & time but they store the values directly in the model. I am trying to store an integer - bat_id so that the bat_id will directly link the review model to the bat model.
Examples I have found that are close:
How do ruby on rails multi parameter attributes really work (datetime_select)
Rails multiple fields to one model attribute
Using multiple input fields for one attribute
Rails Update Single Attribute with Multiple Fields
My form now:
<%= form_for(#review) do |f| %>
<%= render 'shared/error_messages', object: f.object %>
<div class="field" align= "center">
<h3>Select Brand</h3>
<%= f.collection_select :manufacturer_id, Manufacturer.all, :id, :manufacturer, include_blank: true %>
<h3>Select Bat</h3>
<%= f.grouped_collection_select :bat_id, Manufacturer.all, :bats, :manufacturer, :id, :model_year_and_name, include_blank: true %>
<h3>What do you like about this bat?</h3>
<%= f.text_area :pros, placeholder: "Enter what you like..." %>
<h3>What do you not like about this bat?</h3>
<%= f.text_area :cons, placeholder: "Enter what you don't like..." %></br>
</div>
<div align="center">
<%= f.submit "Add Review", class: "btn btn-large btn-info" %>
</div>
<% end %>
I am submitting to the review table and trying to submit both of these to the bat_id attribute.
<h3>Select Brand</h3>
<%= f.collection_select :manufacturer_id, Manufacturer.all, :id, :manufacturer, include_blank: true %>
<h3>Select Bat</h3>
<%= f.grouped_collection_select :bat_id, Manufacturer.all, :bats, :manufacturer, :id, :model_year_and_name, include_blank: true %>
In my bat model I have: has_many :reviews & In my reviews model I have: belongs_to :bat
UPDATE: Is it possible to use a hidden field with the combination of javascript and my two inputs to determine my one output bat_id?
Update I changed my dropdown code to what works so that I enter in manufacturer_id & bat_id when both are selected. However I still think there is a way to store one value in my review model. I am using javascript very similiar to this
From a UI perspective this seems broken... users will be able to associate any model year & name with any manufacturer, even if that manufacturer did not produce that model year & name.
Assuming you will introduce some javascript to handle that, from a rails perspective you will get undefined behavior with two :bat_id fields in the same form. I think you need this:
<h3>Select Brand</h3>
<%= f.collection_select :manufacturer_id, Manufacturer.all, :id, :manufacturer, include_blank: true %>
<h3>Select Bat</h3>
<%= f.collection_select :bat_id, Bat.all, :id, :model_year_and_name, include_blank: true %>
Alternatively you can just create one dropdown containing a composite field, like this:
<h3>Select Bat</h3>
<%= f.collection_select :bat_id, Bat.all.sort {|a, b| a.manufacturer_model_year_and_name <=> b.manufacturer_model_year_and_name}, :id, :manufacturer_model_year_and_name, include_blank: true %>
and then in your Bat model introduce something like this:
def manufacturer_model_year_and_name
"#{self.manufacturer.name}: #{self.model_year_and_name}"
end
As discussed in your other answer, you shouldn't need to store the manufacturer_id on your review model.
I would recommend creating a Manufacturer select that isn't accessed in your Review model, but is simply used to filter the list of bats on the form.
The best way to do this is probably to add some custom data attributes to the Bat select.
<%= collection_select :manufacturer, :manufacturer_id, Manufacturer.all, :id, :manufacturer %>
<%= f.select :bat_id, Bat.all.map{ |b| [b.model_year_and_name, b.id, {'data-manufacturer' => b.manufacturer_id}] } %>
Then use some javascript to filter the Bat select when the Manufacturer select is changed.
Unfortunately you cannot just set display: none to an option element to hide it. This does not hide the option in many browsers. So the best method is to use a bit of jQuery to clone the original select every time the manufacturer select is changed, and remove any option that isn't associated with the selected manufacturer. Like so:
// rename the original select and hide it
$('#bat_id').attr('id', 'bat_id_original').hide();
$('#manufacturer_id').on('change', function() {
$('#bat_id').remove(); // remove any bat_id selects
$bat = $('#bat_id_original')
.clone() // clone the original
.attr('id', 'bat_id') // change the ID to the proper id
.insertAfter('#bat_id_original') // place it
.show() // show it
.find(':not(option[data-manufacturer="' + $(this).val() + '"])')
.remove(); // find all options by other manufacturers and remove them
});
You might need to change a few things to get this to work in your installation, but you can view a static demo on jsFiddle here: http://jsfiddle.net/JL6M5/
You will probably need to reject the manufacturer_id field on form submit, avitevet already pointed out this answer which should help there: Rails: Ignoring non-existant attributes passed to create()

How to set a child attribute as collection_select value?

My form contains the following tag:
<%= f.collection_select :employee_id, #employees, :id, :value, :prompt => true %>
Employee looks like this:
employee
- attr1
- attr2
- user
- firstname
- lastname
My question: How to set the lastname of an employee as the value in the select field? I'm pretty sure it's possible, but I think I have some gaps in the syntax.
Why would you do that?
It's possible, with:
<%= f.collection_select :employee_id, #employees, :last_name, :text_method, :prompt => true %>
Where :text_method is method which called on #employees members will return text that you'd like to appear in dropdown.
You may be asking yourself why I have used <%= f.select %> this is FormOptionHelper on the Ruby on Rails Api this is very similar to collection_select however with that it returns and tags for the collection of values. Whereas select creates a series of contained option tags for provided object and method. So in saying this I believe you could have the following:
<%= f.select(:employee_id, Employee.all.collect { |emp| [emp.lastname, user.id] }
.sort{ |a, b| a[0] <=> b[0] }, {:prompt => "Select a Employee"}) %>
This selects all employees and sorts them in order of their last name.

Resources