How can I select a value from a pre-defined collection in a model, using collection_radio_buttons?
I tried
= f.collection_radio_buttons :lang_lvl, Language.language_levels, :language_level, :language_level #does not work
= f.select :lang_lvl, Language.language_levels, include_blank: true #works
The data source is defined in a model Language.rb:
class Languages < ActiveRecord::Base
LANGUAGE_LEVELS = [:"A1/Elem", :"A2/PreInt", :"B1/Int", :"B2/UpperInt", :"C1/Adv", :"C2/Prof", :"A1/Beg"]
def self.language_levels
LANGUAGE_LEVELS.map { |language_level| [language_level, language_level] }
end
end
The 3rd and 4th arguments of collection_radio_buttons specify the method used to extract value and text respectively. The function will try to call [language_level, language_level].language_level to convert the data because you're passing an array of arrays as a collection and :language_level to be called for each item in that sub-array.
If you don't want to change your model, you could try to change the code to this:
= f.collection_radio_buttons :lang_lvl, Language.language_levels, :first, :last
Related
This question is asked many times on SO. The main problem is nothing got fits into my situation.
Case is, I am not able to store typed content as array in database column.
text_field whose code is:
= text_field_tag 'product[keywords][]', #product.keywords, class: 'tab-input
product_keywords'
In controller strong parameters are:
params.require(:product).permit(:id, :name, :keywords => [])
Jquery code that is not not removing value upon deletion when typed wrong value but it add commas after each element as I want to take commas seperated value in one column.
$(document).on 'keyup', '.product_keywords', ->
keyword = #value.replace(/(\w)[\s,]+(\w?)/g, '$1, $2')
if keyword != #value
#value = keyword
return
model code:
serialize :keywords, Array
migration code:
class AddKeywordsToProducts < ActiveRecord::Migration[5.1]
def change
add_column :products, :keywords, :text
end
end
So, if someone writes, abc and hit space a comma is added in the end. after three typed words it will look like:
abc, dbx, she
now I want to store it as array in column but its not storing properly.
it stores as:
["abc, dbx, she"]
Also please can anybody tell me the best cases to handle these cases?
Plus best practices to deal with such cases using ruby so I will learn it for future?
You probably want a custom serializer as shown here. So instead of:
serialize :keywords, Array
You might do somewhat like:
serialize :keywords, KeywordSerializer
And somewhere in helpers:
class KeywordSerializer
def self.dump(what)
what.join(", ")
end
def self.load(what)
what.split(/\s*,\s*/)
end
end
Passing array elements using single form tag is not possible to pass as a array and passing array as a string, you need to process it near white-listing your params,
permitted_params = params.require(:product).permit(:id, :name, :keywords => [])
permitted_params[:keywords] = permitted_params[:keywords][0].split(/\s*,\s*/)
I'm trying to create a select input that takes specific User models and displays them as a string while saving them as an integer. Typically I would set the enumerable up the same way as below but as a static hash.
While attempting to create a new Product, I keep receiving the following error: "undefined local variable or method `user_business_hash'"
I've tried moving the 'user_business_hash' method to application/products controller, application/products helper with no luck.
Model
enum user_id: user_business_hash
validates :user_id, inclusion: user_ids.keys
def user_business_hash
# output: {User.business_name => User.id }
# ex: {"Business Name A"=>2, "Business Name B"=>1, "Business Name C"=>5}
array = User.where(account_type: 'Business').map{|x| [x.business_name, x.id] }
hash = array.inject({}) do |memo, (key, value)|
memo[key] = value
memo
end
return hash
end
Form
<%= form.select :user_id, Product.user_ids.keys, prompt: 'Select', id: :product_user_id %>
I think that what you actually want is:
on your controller
#options = User.where(account_type: 'Business')
on your view
options_from_collection_for_select(#options, "id", "business_name")
I'm using the active_enum gem in my Rails 4 application. https://github.com/adzap/active_enum
gem 'active_enum'
In my model, I have an enumerate:
class Meeting < ActiveRecord::Base
enumerate :participant_type do
value 0 => 'Juniors'
value 1 => 'Senior'
value 2 => 'Administration'
end
end
How can I use these values in a form select element?
I've tried the following but I get a runtime error:
= f.select :participant_type, Loan.participant_type
undefined method `participant_type' for #<Class:0x007f8803581050>
Make Participant type as a separate class that extends ActiveEnum::Base and try .to_select method.
= f.select :participant_type, ParticipantType.to_select, required: true
If it is Active Record Enum, try the below:
It is a plural participant_types as mentioned in the docs.
= f.select :participant_type, Meeting.participant_types, required: true
So I am trying to implement multiple autocomplete using this gem and simple_form and am getting an error.
I tried this:
<%= f.input_field :neighborhood_id, collection: Neighborhood.order(:name), :url => autocomplete_neighborhood_name_searches_path, :as => :autocomplete, 'data-delimiter' => ',', :multiple => true, :class => "span8" %>
This is the error I get:
undefined method `to_i' for ["Alley Park, Madison"]:Array
In my params, it is sending this in neighborhood_id:
"search"=>{"neighborhood_id"=>["Alley Park, Madison"],
So it isn't even using the IDs for those values.
Does anyone have any ideas?
Edit 1:
In response to #jvnill's question, I am not explicitly doing anything with params[:search] in the controller. A search creates a new record, and is searching listings.
In my Searches Controller, create action, I am simply doing this:
#search = Search.create!(params[:search])
Then my search.rb (i.e. search model) has this:
def listings
#listings ||= find_listings
end
private
def find_listings
key = "%#{keywords}%"
listings = Listing.order(:headline)
listings = listings.includes(:neighborhood).where("listings.headline like ? or neighborhoods.name like ?", key, key) if keywords.present?
listings = listings.where(neighborhood_id: neighborhood_id) if neighborhood_id.present?
#truncated for brevity
listings
end
First of all, this would be easier if the form is returning the ids instead of the name of the neighborhood. I haven't used the gem yet so I'm not familiar how it works. Reading on the readme says that it will return ids but i don't know why you're only getting names. I'm sure once you figure out how to return the ids, you'll be able to change the code below to suit that.
You need to create a join table between a neighborhood and a search. Let's call that search_neighborhoods.
rails g model search_neighborhood neighborhood_id:integer search_id:integer
# dont forget to add indexes in the migration
After that, you'd want to setup your models.
# search.rb
has_many :search_neighborhoods
has_many :neighborhoods, through: :search_neighborhoods
# search_neighborhood.rb
belongs_to :search
belongs_to :neighborhood
# neighborhood.rb
has_many :search_neighborhoods
has_many :searches, through: :search_neighborhoods
Now that we've setup the associations, we need to setup the setters and the attributes
# search.rb
attr_accessible :neighborhood_names
# this will return a list of neighborhood names which is usefull with prepopulating
def neighborhood_names
neighborhoods.map(&:name).join(',')
end
# we will use this to find the ids of the neighborhoods given their names
# this will be called when you call create!
def neighborhood_names=(names)
names.split(',').each do |name|
next if name.blank?
if neighborhood = Neighborhood.find_by_name(name)
search_neighborhoods.build neighborhood_id: neighborhood.id
end
end
end
# view
# you need to change your autocomplete to use the getter method
<%= f.input :neighborhood_names, url: autocomplete_neighborhood_name_searches_path, as: :autocomplete, input_html: { data: { delimiter: ',', multiple: true, class: "span8" } %>
last but not the least is to update find_listings
def find_listings
key = "%#{keywords}%"
listings = Listing.order(:headline).includes(:neighborhood)
if keywords.present?
listings = listings.where("listings.headline LIKE :key OR neighborhoods.name LIKE :key", { key: "#{keywords}")
end
if neighborhoods.exists?
listings = listings.where(neighborhood_id: neighborhood_ids)
end
listings
end
And that's it :)
UPDATE: using f.input_field
# view
<%= f.input_field :neighborhood_names, url: autocomplete_neighborhood_name_searches_path, as: :autocomplete, data: { delimiter: ',' }, multiple: true, class: "span8" %>
# model
# we need to put [0] because it returns an array with a single element containing
# the string of comma separated neighborhoods
def neighborhood_names=(names)
names[0].split(',').each do |name|
next if name.blank?
if neighborhood = Neighborhood.find_by_name(name)
search_neighborhoods.build neighborhood_id: neighborhood.id
end
end
end
Your problem is how you're collecting values from the neighborhood Model
Neighborhood.order(:name)
will return an array of names, you need to also collect the id, but just display the names
use collect and pass a block, I beleive this might owrk for you
Neighborhood.collect {|n| [n.name, n.id]}
Declare a scope on the Neighborhood class to order it by name if you like to get theat functionality back, as that behavior also belongs in the model anyhow.
edit>
To add a scope/class method to neighborhood model, you'd typically do soemthing like this
scope :desc, where("name DESC")
Than you can write something like:
Neighborhood.desc.all
which will return an array, thus allowing the .collect but there are other way to get those name and id attributes recognized by the select option.
I have an array
ABC = ["A","B","C"]
<%= f.select :abc, model::ABC, :include_blank => true %>
If I select C, then I want to display an input field for "city" and "state". Otherwise, those fields should be hidden. Is there any simple way of doing this. I don't want to use jQuery or Ajax.
I don't know of a way to change what fields are being displayed without using javascript.
What you could do is always display the city and state fields, but only require them if the select menu is set to C. For example, define a validation rule that requires a field if the select menu is set to C. In your lib/ directory, make require_if_c_validator.rb
class RequireIfCValidator < ActiveModel::EachValidator
def validate_each object, attribute, value
if object.your_attribute_name == 'C' && value == nil
object.errors[attribute] < 'is required'
end
end
end
And then in your model, call it on city and state:
validate :city, :require_if_c => true
validate :state, :require_if_c => true