Where to put a class on the f.select in Ruby? - ruby-on-rails

I am trying to apply a class to a select form element that is built with Ruby.
Here is the f.select:
<%= f.select :within, ["5km", "50km", "100km", "250km", "500km", "1000km"], { :include_blank => 'Anywhere', :selected => (params[:listing][:within] unless params[:listing].nil? || !params[:listing].has_key?(:within)) } %>
Where does the class go? My class is:
:class => 'input-medium'
Thanks!

See the documentation, specifically the html_options argument.
Add yet another argument onto the end of what you've posted in your question with:
{ class: "input-medium" }
Altogether that's
<%= f.select :within, ["5km", "50km", "100km", "250km", "500km", "1000km"], { :include_blank => 'Anywhere', :selected => (params[:listing][:within] unless params[:listing].nil? || !params[:listing].has_key?(:within)) }, { class: "input-medium" } %>

Related

Rails undefined method `map' for nil:NilClass on collection_select - The reference called may be nil

After reading through the other SO answers, it's pretty clear that there are some common themes.
Mostly this type of error seems to happen when the object called is not defined yet, but in this case we have a has_many relationship that may not have a referenced entry when building the selection.
Class Tag
property :name, type: String
has_many :in, :tagged, type: :CONCEPTUAL_TAG, model_class: :Artefact
end
One option is to do something like this:
<div class="field">
<%= f.label :tagged_id %><br>
<% if !#tag.tagged.empty? %>
<%= f.collection_select(:tagged, Artefact.all.sort { |a,b| a.name <=> b.name }, :id, :name, options = {:prompt => "Please Select an Item", :selected => #tag.tagged.map(&:id)}, html_options = {:multiple => true, :class=>"search"}) %>
<% else %>
<%= f.collection_select(:tagged, Artefact.all.sort { |a,b| a.name <=> b.name }, :id, :name, options = {:prompt => "Please Select an Item"}, html_options = {:multiple => true, :class=>"search"}) %>
<% end %>
</div>
But this is definitely not DRY.
Is there a way to select nothing when there is no association, and pre-populate when there is while keeping to a single f.collection_select?
You don't have to use the condition in your view. Even if there are no items in #tag.tagged, it is still an ActiveRecord::Relation instance, so you can go ahead and just call map on it:
<%= f.collection_select(:tagged,
Artefact.all.sort { |a,b| a.name <=> b.name },
:id,
:name,
options = {
:prompt => "Please Select an Item",
:selected => #tag.tagged.map(&:id)
},
html_options = {
:multiple => true,
:class=>"search"
})
%>

Simple form array of hashes

I have a form that updates an #identity object. Two #identity attributes need to be assigned from a separate collection #accounts.
#accounts = [{
'name' => 'A',
'page_id' => 1},
{
'name' => 'B',
'page_id' => 2
}]
The form needs display the name, but pass both the name and page_id.
<%= simple_form_for(#identity, :remote => true) do |f| %>
<%= f.input :page_name, :collection => #accounts.map { |a| a['name'] }, :as => :radio_buttons, :item_wrapper_tag => :li %>
<%= f.submit 'Submit', :class => 'btn btn-primary' %>
<% end %>
How can I also pass the matching page_id attribute without displaying it?
:collection => #accounts.map { |e| [e['name'], e['page_id']] }

dropdown list in ruby on rails

I want to display values to drop down list from Database.
For that in my controller class i did the following to get the values from db and its getting properly.
#value = Message.find(:all)
<Message ID: 14448, SlNo: 609">, #<Message ID: 14448, SlNo: 610">
How can i display the SlNo values to drop down list.Here is the code am using and getting error!I don't know how to set values inside a collection_select.Please help me!!
<% #value.each do |d| %>
<%=collection_select(:value, :id, #value, :id, { selected: params.fetch(:value, {})[:id].to_i, :prompt => "-Select a device" }) %>
<% end %>
Table names getting
["UniqueDeviceID", "SlNo"]
Model
class MessageDetail < ActiveRecord::Base
# attr_accessible :title, :body
set_table_name 'DeviceDetails'
set_primary_key 'SlNo'
end
instead of
<% #value.each do |d| %>
<%=collection_select(:value, :id, #value, :id, { selected: params.fetch(:device, {})[:id].to_i, :prompt => "-Select a device" }) %>
<% end %>
use
<%= collection_select :value, :id, #value, :id, :S1No, { selected: params.fetch(:device, {})[:id].to_i, :prompt => "-Select a device" } %>
UPDATE: explanations for passed parameters
:value = a symbol representation of the record you want to update, it may also not be an instance record but just a symbol that will be used in the naming convention of the select tag
:id = the column that you wish to update
#value = the collection to show the choices
:id = the method you want to use that will be passed as the value of the selected value
:S1No = the method that will be used as the label for the options of the select tag
# START
f.collection_select :id, Message.all(:order => "name"), :id, :name, :include_blank => true
# END
OR
# START
messages_arr = []
messages = Message.all(:order => "name")
messages.each do |msg|
messages_arr << [msg.name, msg.id]
end
f.select(:id, options_for_select(messages_arr), {:include_blank => 'Include All'}, {:class=>"span12"})
# END

Delete a rails relationship with an :include_blank

I want to be able to select a blank object and post it so that I can clear a rails relationship. By default when you POST while selected on the :include_blank entry, nothing gets posted so it doesn't delete the old relationship. So I'm trying to add a 0 id blank item to the array.
Original:
<%= select_f f,:config_template_id, #operatingsystem.config_templates.where(:template_kind_id => f.object.template_kind_id), :id, :name, {:include_blank => true}, { :label => f.object.template_kind } %>
Mine:
<%= select_f f,:config_template_id, #operatingsystem.config_templates.where(:template_kind_id => f.object.template_kind_id).collect { |c| [c.name, c.id]}.insert(0, ['', 0]) %>
I'm getting a "wrong number of arguments (3 for 5)" error though and can't figure out what I'm missing. Any pointers? (I also can't find select_f anywhere on web, I think google ignores the _ so the search is way to open ended... For rails 3 should I be using something else?)
You've omitted the 3rd, 4th, 5th, and 6th argument you were passing into your original code block. Whatever select_f is, it's expecting at least 5 arguments. In the original you pass the following to select_f (one argument per line for clarity)
f,
:config_template_id,
#operatingsystem.config_templates.where(:template_kind_id => f.object.template_kind_id),
:id,
:name,
{:include_blank => true},
{ :label => f.object.template_kind }
In your new (broken) call you're only passing
f,
:config_template_id,
#operatingsystem.config_templates.where(:template_kind_id => f.object.template_kind_id).collect { |c| [c.name, c.id]}.insert(0, ['', 0])
Use your first method call, just replace the 3rd argument.
f,
:config_template_id,
#operatingsystem.config_templates.where(:template_kind_id => f.object.template_kind_id).collect { |c| [c.name, c.id]}.insert(0, ['', 0])
:id,
:name,
{:include_blank => true},
{ :label => f.object.template_kind }
Finally, if you don't want the :include_blank => true to be passed, but still do want the label, just pass nil or {} to the 5th argument
f,
:config_template_id,
#operatingsystem.config_templates.where(:template_kind_id => f.object.template_kind_id).collect { |c| [c.name, c.id]}.insert(0, ['', 0])
:id,
:name,
nil,
{ :label => f.object.template_kind }
And altogether on one line:
<%= select_f f, :config_template_id, #operatingsystem.config_templates.where(:template_kind_id => f.object.template_kind_id).collect { |c| [c.name, c.id]}.insert(0, ['', 0]), :id, :name, nil, { :label => f.object.template_kind } %>
I can't guarantee this works because I don't know where the API for select_f is or if you created it yourself. This should however nudge you in the right direction.

Refactoring of many if statements for params[...], inside controller action

I have such code, for making chain selects in my form
View for index action:
<%= form_tag do %>
<%= collection_select(*#brands_select_params) %>
<%= collection_select(*#car_models_select_params) %>
<%= collection_select(*#production_years_select_params) %>
<% # Пока еще никто ничего не выбрал %>
<%= submit_tag "Send", :id => "submit", :name => "submit" %>
And my controller:
class SearchController < ApplicationController
def index
#brands = Brand.all
#car_models = CarModel.all
if (params[:brand].blank?)
#brands_select_params = [:brand, :id, #brands, :id, :name, :prompt => "Выбирай брэнд"]
if params[:car_model].blank?
#car_models_select_params = [:car_model, :id, #car_models, :id, :name, { :prompt => "Model" }, \
{ :disabled => "disabled" }]
#production_years_select_params = [:production_year, :id, #car_models, :id, :name, { :prompt => "Year" }, \
{ :disabled => "disabled" }]
end
else
#brands_select_params = [:brand, :id, #brands, :id, :name, { :selected => params[:brand][:id] } ]
if params[:car_model].blank?
#car_models_select_params = [:car_model, :id, Brand.find(params[:brand][:id]).car_models, :id, :name, \
{ :prompt => "And model now" } ]
#production_years_select_params = [:production_year, :id, #car_models, :id, :name, { :prompt => "Year" }, \
{ :disabled => "disabled" } ]
else
#car_models_select_params = [:car_model, :id, Brand.find(params[:brand][:id]).car_models, :id, :name, \
{ :selected => params[:car_model][:id] } ] unless params[:car_model][:id].empty?
#production_years_select_params = [:production_year, :id, CarModel.find(params[:car_model][:id]).production_years, :id, :year, \
{ :prompt => "And year now" } ] unless params[:car_model][:id].empty?
end
end
end
end
As you can see, too many ifs in my controller code. And i gonna add more conditions there. After that anyone who read that code will get brain corruption. So i just wanna make it in real Ruby way, but don't know how. Please, help, guys. How should i refactor this bunch of crap?
I think a big part of the problem is you're doing too much in your controller. Generating markup (and IMO that includes building parameter lists for form helpers) should be done in views and view helpers. So:
module SearchHelper
def brand_select brands, options={}
collection_select :brand, :id, brands, :id, :name, :options
end
def car_model_select car_models, options={}
collection_select :car_model, :id, car_models, :id, :name, options
end
def production_year_select years, options={}
collection_select :production_year, :id, years, :id, :year, options
end
end
Then you can cut your controller down to this:
def index
#brands = Brand.all
#car_models = CarModel.all
#selected_brand_id = params[:brand] && params[:brand][:id]
#selected_car_model_id = params[:car_model] && params[:car_model][:id]
#production_years = #selected_car_model_id ?
[] : CarModel.find(#selected_car_model_id).production_years
end
And in your view:
<%= brand_select #brands, :prompt => "Выбирай брэнд",
:selected => #selected_brand_id
%>
<%= car_model_select #car_models, :prompt => "Model",
:selected => #selected_car_model_id
%>
<%= production_year_select #production_years, :prompt => "Year",
:selected => #selected_car_id
%>
I suspect you could simplify this even more using form_for and fields_for and get rid of the helpers entirely, but it depends a bit on how your model associations are set up.
There is no obvious solution to this kind of problem.
Generally, I try to keep the if / else architecture very clear and export all code into separate methods. 2 advantages here:
readability
easier unit testing
For your case, it would be:
class SearchController < ApplicationController
def index
#brands = Brand.all
#car_models = CarModel.all
if (params[:brand].blank?)
#brands_select_params = [:brand, :id, #brands, :id, :name, :prompt => "Выбирай брэнд"]
if params[:car_model].blank?
#car_models_select_params, #production_years_select_params = get_card_model(#car_models)
end
else
#brands_select_params = [:brand, :id, #brands, :id, :name, { :selected => params[:brand][:id] } ]
if params[:car_model].blank?
#car_models_select_params, #production_years_select_params = foo_method(#car_models)
else
#car_models_select_params, #production_years_select_params = bar_method
end
end
end
def get_card_model car_models
[
[:car_model, :id, car_models, :id, :name, { :prompt => "Model" }, { :disabled => "disabled" }],
[:production_year, :id, car_models, :id, :name, { :prompt => "Year" }, { :disabled => "disabled" }]
]
end
end

Resources