Make select list output in alphabetical order in Rails? - ruby-on-rails

I have a select list in my model which lists a persons name with their employers name:
<li>Case Handler Name<span><%= f.select :person_id, Person.all.collect { |x| [x.name_and_company, x.id] } %></span></li>
def name_and_company
return "#{personname} (#{company})"
end
Is it possible to force the select list to output in alphabetical order?
I am assuming I would put an order tag in there...somewhere?
(:order => 'personname DESC')
Thanks,
Danny

You can do it like this
# controller
#people = Person.order_by('personname ASC').collect {|x| [x.name_and_company, x.id] }
# model
named_scope :order_by, lambda { |o| {:order => o} }
# view
<%= f.select :person_id, #people %>

Answer:
#users_controller.rb
def index
#people = Person.alphabetically
end
#user.rb
scope :alphabetically, order("name ASC")
#index.haml
= f.select :person_id, #people.all.collect { |p| [p.name, p.id] } %>

Even easier ...
<%= f.select :name, options_from_collection_for_select(Person.order("name ASC"), :name, :name), :prompt => 'Select' %>
Note: Doesn't require custom methods or additions to controller

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"
})
%>

Active Admin checkboxes not selected when edit model

I'm using a custom collection to display checkboxes with schedule. It saves, but when I try to edit it returns to me unchecked. Why?
f.inputs for: :schedule, name: 'Employee Schedule' do |sf|
sf.input :sunday, as: :check_boxes, collection: available_hours, method: :to_s
sf.input :monday, as: :check_boxes, collection: available_hours, method: :to_s
sf.input :tuesday, as: :check_boxes, collection: available_hours, method: :to_s
sf.input :wednesday, as: :check_boxes, collection: available_hours, method: :to_s
sf.input :thursday, as: :check_boxes, collection: available_hours, method: :to_s
sf.input :friday, as: :check_boxes, collection: available_hours, method: :to_s
sf.input :saturday, as: :check_boxes, collection: available_hours, method: :to_s
end
def available_hours
(0..23).map { |h| ["#{h}h às #{h.next}h", h] }
end
helper_method :available_hours
I found a solution for this question
My collection remains unaltered
def available_hours
Array(0..23)
end
And my form will have a :member_label parameter receiving a Proc that will change it after collection already gathered
member_label: Proc.new { |h| "#{h}h às #{h.next}h" }
After modifications:
sf.input :sunday, as: :check_boxes, collection: available_hours, member_label: Proc.new { |h| "#{h}h às #{h.next}h" } , method: :to_s
and so on...
You need to determine which checkbox is selected like this: ["#{h}h às #{h.next}h", h, :selected]
def available_hours(_h)
(0..23).map { |h| ["#{h}h às #{h.next}h", h, h == _h ? :selected : ''] }
end
sf.input :sunday, as: :check_boxes, collection: available_hours(sh.object.sunday), method: :to_s
...or something similar.
It could be a different situation/need, but I think what I did for one of my project is possible a solution. I created a custom FormStatic input class that can be used for an ActiveAdmin edit form.
module ActiveAdmin
module Inputs
class ProductsInput < ::Formtastic::Inputs::CheckBoxesInput
def choice_html(choice)
html_options = label_html_options.merge(
:for => choice_input_dom_id(choice),
:class => checked?(choice[1]) ? 'checked' : nil
)
template.content_tag(:label, choice_label(choice), html_options)
end
def collection
super.sort {|a, b| a[0] <=> b[0]}
end
def choice_label(choice)
name, id = choice
product = Product.find(id)
name = ''
name << template.content_tag(:span, product.human_state_name, class: 'status_tag important') + ' ' unless product.on_sale?
name << product.name
(hidden_fields? ?
check_box_with_hidden_input(choice) :
check_box_without_hidden_input(choice)) + \
template.image_tag(product.listing.thumb, width: 60).html_safe + \
template.content_tag(:span, name.html_safe, class: 'choice_label')
end
end
end
end
Then you can use it in an edit block like this:
ActiveAdmin.register Collection do
form do |f|
f.inputs 'Products' do
f.input :products, as: :products
end
end
end
Collection has_many products through collection_products.

Extract search functionality to Form Object in Rails

In my Rails application I have simple search functionality. I want to extract to Form Object but don't know how to do. I have search form which looks like this:
.row
= horizontal_simple_form_for :cars, {url: cars_path, method: :get} do |f|
.col-md-4
.row
.col-md-12
= f.input :handover_location, label: I18n.t('.handover'), collection: Location.all.map{|hl| [hl.location_address, hl.id]}
= f.input :return_location, label: I18n.t('.return') ,collection: Location.all.map{|rl| [rl.location_address, rl.id]}
= f.input :car_class, label: I18n.t('.car_class') ,collection: CarClass.all.map { |c| [c.name, c.id] }, include_blank: true
.col-md-4
= f.input :handover_date, as: :string, label: false
= f.input :return_date, as: :string, label: false
= f.submit class: 'btn btn-success'
Cars controller:
class CarsController < ApplicationController
skip_authorization_check
def index
#cars = Car.search(params)
end
def show
end
end
And class method in Car model which search correct cars:
def self.search(params)
self.joins(:reservations).where.not("reservations.reception_time <= ? AND reservations.return_time >= ?",
params[:cars][:return_date], params[:cars][:handover_date]).
joins(:car_class).where("car_classes.id= ?", params[:cars][:car_class])
.cars_at_both_locations(params[:cars][:handover_location], params[:cars][:return_location])
end
Now I'm trying to extract this to Form Object. I've created a file search_form.rb:
class SearchForm
include ActiveModel::Model
attr_accessor :handover_date, :return_date, :handover_location, :return_location, :car_class
end
But now I don't know how to handle my params to this form object. Thank's in advance.
I wish I could help you with the Form Object stuff, but I need to learn more about classes & modules
I can help you with the search functionality, as we've done it before here
Here's the code we used:
#View
<%= form_tag search_path, :method => :post, :id => "SearchForm" do %>
<%= text_field_tag :search, params[:search], placeholder: 'Search your favourite products or brands', :autocomplete => :off %>
<%= image_submit_tag 'nav_bar/search.png' %>
<% end %>
#config/routes.rb
match 'search(/:search)', :to => 'products#search', :as => :search, via: [:get, :post]
#app/controllers/products_controller.rb
def search
#products = Product.search(params[:search])
respond_to do |format|
format.js { render :partial => "elements/livesearch", :locals => {:search => #products, :query => params[:search]} }
format.html { render :index }
end
end
Notice the form_tag we used?
Simple form does not work with form_tag currently (it requires an object) - we just send the data with a GET request to the controller & that then sends the data to the Product model
I think your problem will be caused by the use of your SearchForm object. You only need this because your use of simple form means you have to pass an object. Problem being this is not necessary for search
A better way will be to use a standard form_tag, and send the request directly to your controller. This will allow you to process the data as params, which you'll be able to send directly to your Car model
--
I can write some code specific to you if you want
I found solution on my own.
Cars controller:
def index
#search_form = SearchForm.new(params[:search_form])
#cars = #search_form.submit(params[:search_form])
end
search_form.rb:
class SearchForm
include ActiveModel::Model
attr_accessor :handover_date, :return_date, :handover_location, :return_location, :car_class
def submit(params)
Car.search(params)
end
end
Search form in view:
.row
= horizontal_simple_form_for SearchForm.new, {url: cars_path, method: :get} do |f|
.col-md-4
.row
.col-md-12
= f.input :handover_location, label: I18n.t('.handover'), collection: Location.all.map{|hl| [hl.name, hl.id]}
= f.input :return_location, label: I18n.t('.return') ,collection: Location.all.map{|rl| [rl.name, rl.id]}
= f.input :car_class, label: I18n.t('.car_class') ,collection: CarClass.all.map { |c| [c.name, c.id] }, include_blank: true
.col-md-4
= f.input :handover_date, as: :string, label: false
= f.input :return_date, as: :string, label: false
= f.submit class: 'btn btn-success'
search method in car model:
def self.search(params)
self.joins(:reservations).where.not("reservations.reception_time <= ? AND reservations.return_time >= ?",
params[:return_date], params[:handover_date]).
joins(:car_class).where("car_classes.id= ?", params[:car_class])
.cars_at_both_locations(params[:handover_location], params[:return_location])
end

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

Ancestry: skip element from scope

Using gem ancestry.
How skip self element from Category::ActiveRecordRelation or need use scope?
= simple_form_for #category do |f|
= f.input :parent_id, collection: Category.roots
Something like:
= f.input :parent_id, collection: Category.roots.except(#category)
= f.input :parent_id, collection: Category.roots.where("id <> ?", #category.id)
or via a scope
category.rb
scope :except, lambda{ |category| where("id <> ?", category.id) }
then
= f.input :parent_id, collection: Category.roots.except(#category)

Resources