I'm building a form where users create a financial transaction. One field is a drop down list of bank accounts.
I would like this dropdown list to group all listed Bank Accounts by each account's account type (BankAccount::ACCOUNT_TYPE - an attribute in each BankAccount record).
If I manually code everything right now, the code would look like this:
<%= f.select :bank_account_id,
{
'On-Budget' => ['Cash',
'Credit Card 1',
'Credit Card 2',
'Venmo'],
'Off-Budget' => ['Investment Bank 1',
'Investment Bank 1'],
'Closed' => ['Old Bank 1',
'Old Bank 2'],
} %>
app/models/bank_account.rb - where I define ACCOUNT_TYPES
class BankAccount < ApplicationRecord
ACCOUNT_TYPES = %w(On-Budget Off-Budget Closed).freeze
...
end
Here is my working collection.select, without grouping
<%= f.collection_select :bank_account_id,
BankAccount.all,
:id,
:name,
{prompt: 'Select an account'} %>
From the rails API, I think grouped_collection_select is what I need
(https://api.rubyonrails.org/classes/ActionView/Helpers/FormBuilder.html#method-i-grouped_collection_select)
grouped_collection_select(method, collection, group_method, group_label_method, option_key_method, option_value_method, options = {}, html_options = {})
Using BankAccount::ACCOUNT_TYPES as my group_method, and group_label_method doesn't work.
<%= f.grouped_collection_select(:bank_account_id,
BankAccount.all,
BankAccount::ACCOUNT_TYPES, (group_method?)
BankAccount::ACCOUNT_TYPEs, (group_label_method?)
:id,
:name,
{prompt: 'Select an account' } %>
I found the answer from this SO question: https://stackoverflow.com/questions/36854817/use-grouped-collection-select-to-group-a-model-by-enum
I created a helper method
def grouped_bank_account_options_for_select(selected_bank_account_id = nil)
options = {}
BankAccount::ACCOUNT_TYPES.each do |t|
unless t == "Closed"
options[t] = BankAccount.where(account_type: t)
.pluck(:name, :id)
end
end
grouped_options_for_select(options, selected_bank_account_id)
end
and then called it in the view's form helper
<%= select_tag :bank_account_id, grouped_bank_account_options_for_select(params[:bank_account_id]), {prompt: 'Select an account'} %>
Related
I have created drop down list for employees.
What I want?
I want to select full Name for each one of them.
Type of form:
I use simple_form.
I actually have:
= f.input :person_id, label: "Employee", collection: #employee, prompt: "Select employee"
Result(I know, that is reference):
Before I use collection_select, but simple_form doesn't support validation for this type of collection.
Code for collection_select. This type of drop down list displays properly full name.
= f.collection_select :person_id, #employee, :id, :fullName, {prompt: "Wybierz pracownika"}, {class: "form-control"}
Update:
fullName is a method in a person.rb model.
def fullName
"#{first_name} #{last_name}"
end
Object employee.
#employee = Person.where.not(type: "Client")
The easiest way to do this is :
= f.select :person_id, options_for_select(#employees.map{|e| [e.fullName, e.id]}), {:prompt=>"Wybierz pracownika", :required => true}
It will show full names as select options and will dispatch ids as values with form.
You follow this code like below:
<%= f.collection_select(:person_id, Model.all, :person_id, :fullName,{:prompt=>"Wybierz pracownika"}, {:class => 'form-control'}) %>
You will replace your model_name.
Or
= f.input :person_id, label: "Employee", collection: #employee.fullName, prompt: "Select employee"
I think will help you
I'm working on learning Rails 4 via several tutorials, and building a demo app.
I have a table called players that links to a team table. The team has many players, a player has only one team. So I'm using a collection_select tag to pull the team data into the player form.
It looks like this:
<%= collection_select :player, :team_id, Team.find(:all), :id, :name, options ={:prompt => "Select a team"} %>
This works fine-- but I'd like to have the format look like "Team Name: Team City"-- I can't figure out how to concatenate the :name and :city values in the tag however. Is this possible?
Create a method in your Team model like the following one
def name_with_city
"#{name}: #{city}"
end
Then use it as below
<%= collection_select :player, :team_id, Team.find(:all), :id, :name_with_city, {:prompt => "Select a team"} %>
Find out more about collection_select in the documentation
You can format the collection to your liking as:
<%= collection_select :player,
:team_id,
Team.find(:all).collect { |t| [ t.id, "#{t.name}: #{t.city}" ] },
:first,
:last,
{ prompt: "Select a team" } %>
The :id and :name parameters have been replaced with first and last signifying the value_method to be first and text_method to be last elements of each array.
If your form is looking up values based on parameters and you need those, the model method is not very convenient. Assuming your controller has a #searched_record method, you can do something like
<% #base_params = #searched_record.one_value << "=>" << #searched_record.second_value << "; " << (l(#searched_record.starts, :format => :long)) << ", " << #searched_record.other_value.description %>
<%= f.text_area :content, :rows => 5, value: #base_params %>
I've been trying to get some dynamic select functionality working, but despite many different tutorial i've yet to get it to work. For ease of reading, i've brought the code examples down to basics. Any advise would be greatly appreciated.
On the faults page, i need to assign a company and contact to the fault, but I only want to be able to see the contacts associated with the selected company
Fault - belongs_to :company, :user, :contact
User - has_many :faults
Contacts - has_and_belongs_to_many :companies
Company - has_and_belongs_to_many :contacts, has_many :faults
/faults/_form.html.erb
<%= f.label :company, "Company:" %>
<%= collection_select(:fault,:company_id,#companies,:id,:full_name, :prompt => "Please select a company") %></br>
<%= f.label :contact, "Contact:" %>
<%= f.collection_select :contact_id, #contacts, :id, :name, :prompt => "Select a Contact" %>
<%= link_to "Add New Contact", {:controller => "companies", :action => "index"}, :confirm => "To add a contact for a company you need to do this from the companies page." %></br>
Gotcha. Using UJS you can dynamically populate your select in 5 steps.
Add class names to your selects in the view
Add JavaScript (or CoffeeScript) to watch the changes on the select element
Create a controller method to fetch the info you need based on your selection
Add a route to match your new controller method
Create a UJS view to update your contacts select
So,
Add class names:
<%= f.label :company, "Company:" %>
<%= collection_select(:fault,:company_id,#companies,:id,:name, {:prompt => "Please select a company"}, {:class => "company_selection"}) %>
<%= f.label :contact, "Contact:" %>
<%= f.collection_select :contact_id, #contacts, :id, :name, {:prompt => "Select a Contact"}, {:class=>"contact_selection"} %>
Throw in some CoffeeScript (app/assets/javascripts/faults.js.coffee)
$(document).ready ->
$(".company_selection").on "change", ->
$.ajax
url: "/faults/get_contacts"
type: "GET"
dataType: "script"
data:
company_id: $('.company_selection option:selected').val()
Update your faults controller
def get_contacts
#company = Company.find params[:company_id]
#contacts = #company.contacts
end
Add a route to your new method
resources :faults do
collection do
get 'get_contacts', to: "faults#get_contacts"
end
end
Add the UJS file (app/views/faults/get_contacts.js.erb)
$('.contact_selection').empty();
$('.contact_selection').append( $('<option>Select the Contact</option>'));
<% #contacts.each do |contact| %>
$('.contact_selection').append($('<option value="<%= contact.id %>"><%= contact.name %></option>'));
<% end %>
Vanilla JS Option
This can be achieved with vanilla javascript only. Make sure that there is a route at companies/[id]/contacts.json which returns correct data.
const select = document.querySelector('#company_id');
select.addEventListener('change',function(e) {
axios.get(`/companies/${e.target.value}/contacts.json`)
.then((res) => {
let contactSelect = document.querySelector('#contact_id')
contactSelect.innerHTML = ''
res.data.map((model, i) => {
contactSelect.options[i] = new Option(model.name, model.id);
})
})
});
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.
I have the following models in my RoR project:
scope and project_scopes.
Project has_many :scopes, through: :project_scopes. Also project accepts_nested_attributes_for :project_scopes.
I add scopes to projects by several selects:
projects/_form.html.haml
= form_for(#project) do |f|
= f.fields_for :project_scopes do |builder|
= render 'project_scope_fields', f: builder
= link_to_add_fields 'Add scopes', f, :project_scopes
projects/project_scope_fields.html.haml
= f.select :scope_id, options_from_collection_for_select(#scopes, "id", "name"), {include_blank: true, class: "project_scopes"}
= f.hidden_field :_destroy
This successfully creates projects with all scopes. When I click edit, it renders same form and displays all scope selects, but they do not have the correct selected value.
How do I fix this?
Look at the documentation for options_from_collection_for_select: It takes 4 parameters, the last one being the selected option. You're not supplying that. Try this:
= f.select :scope_id, options_from_collection_for_select(#scopes, "id", "name", #project.scope)
or simply use the collection_select helper:
= f.collection_select(:scope_id, #scopes, :id, :name)
Try following (and I'm assuming, you're setting attr_accessible correctly):
= f.select :scope_id, #scopes.map{|s| [s.name, s.id]}, {include_blank: true, class: "project_scopes"}
Btw - Scope may not be best model name.