If statement with form field select in rails - ruby-on-rails

I'm trying put an if statement directly into a select field in rails, with no success.
Here is what I've tried:
<%= f.select (:book_id,{
if #a!=1
"Harry Potter", 1,
end
if #b!=2
"Lord of the Rings", 2,
end
end %>`
Any ideas?

Don't do this. It's ugly, and not fun for you to maintain. Also, no good trying to put if-statements or anything other than hash values inside a hash declaration. How about a helper?
Helper code (untested):
def book_select(f)
options = {}
options['Harry Potter'] = 1 unless #a == 1
options['Lord of the Rings'] = 2 unless #b == 2
f.select :book_id, options
end
View code:
<%= book_select(f) %>

Related

Rails 5 Checkbox filter

I can't seem to find a simple solution to this anywhere. Simply put, I just want to be able to filter items with checkboxes and have the option to select multiple boxes and the filter work. The parameters get inputted, but the results don't appear (see below).
If I remove the [] from view, I can get single items to search but not multiples. The [] encodes %5B%5D into the URL which I think is part of reason search won't work. Please anyone who can help.
Controller:
#cars = if params[:colour_category]
Car.where('colour_category LIKE ? OR design LIKE ?', "%#{params[:colour_category]}%", "%#{params[:design_category]}%")
else
Car.all
end
View:
<%= form_tag cars_path, method: :get do %>
<%= check_box_tag('colour_category[]', "Yellow") %>
<%= check_box_tag('colour_category[]', "Blue") %>
<%= check_box_tag('design_category[]', "Luxury") %>
<%= submit_tag "Submit" %>
<% end %>
Example of parameters in server after doing a submit: Parameters: {"utf8"=>"✓", "colour_category"=>["Yellow", "Blue"], "commit"=>"Submit"}
A cleaner approach, which chains different scope/filter methods based on the params values. I'm posting an example from one of my apps, you can adapt the same concept:
module Client::Filterable
extend ActiveSupport::Concern
include PgSearch::Model
included do
scope :find_by_company, -> (company_id) { where(company_id: company_id) }
pg_search_scope :search, against: %w( name ), using: {
tsearch: { prefix: true }
}
end
class_methods do
def filter(params = {})
relation = self
relation = relation.search(params[:query]) if params.fetch(:query, false).present?
relation = relation.find_by_company(params[:company_id]) if params.fetch(:company_id, false).present?
relation.all
end
end
end
then in your controller:
#clients = Client.all.filter(query: 'John Smith', company_id: 4356)
You can add as many filter options into the #filter method, they'll only be applied if the relevant param key is present.
def filter(params = {})
relation = self
relation = relation.by_color_category(params[:color_category]) if param.fetch(:color_category, false).present?
relation = relation.by_design_category(params[:design_category]) if param.fetch(:design_category, false).present?
relation.all
end

If item_id has params - rails

Trying to check to see if two params are in an id field for an if statement in my rails app.
For example i want to state if both [params[:one], params[:two]] exist in column :item_id then continue, if not, don't include in results.
So in my example below it would only chose item_id one as both one and two params exist in the item_id.
Table
item_id | one | two
1 | 2300 | 2255
1 | 2000 | 2304
2 | 2300 | 2000
3 | 2255 | 2222
View Form code
<%= form_tag vpc_search_path do %>
<%= select_tag :one, options_from_collection_for_select(#variety, "variety_id", "variety_name", :selected => "2300"), include_blank: false %>
<%= select_tag :two, options_from_collection_for_select(#variety, "variety_id", "variety_name", :selected => "2255"), include_blank: false %>
<%= submit_tag "Compare" %>
<% end %>
Controller code
if Result.where(trial_id: [params[:one], params[:two]])
#comparison = Result.where('variety_id' => [params[:one], params[:two]], 'year' => params[:year])
else
redirect_to vpc_index_url, notice: "error."
end
probably you should move this check in model
if self.where(item_id: [params[:one] && params[:two]]).present?
then do this
else
do this
end
or probably if you can provide params hash structure I can improve my answer
I'm not sure if I understand that right, but try this:
if SomeModel.where(item_id: [params[:one],params[:two]]).count == 2
#params :one and :two are on the item_id column
else
#one or both are not on the item_id column
end
please explain your problem better, it's hard to understand
Further to the two answers which I upvoted already, you may want to consider this:
You'd use the where function to create the logic, but you need to consider how you're going to call this. Here's how I would do it:
Custom Validator
Might be a bit long-winded, but you may be able to create a custom validator, which you'll be able to call to determine if the params exist in the db:
class TableValidator < ActiveModel::Validator
def validate(record)
unless self.where(item_id: [record.one && record.two]).present?
record.errors[:name] << 'Need a name starting with X please!'
end
end
end
class Table
include ActiveModel::Validations
validates_with TableValidator
end
I used Magnum's code for the logic & the Rails guide for the custom validator
This probably won't work out of the box, but it's what I would look at

Multiple identical collection_select tags in a form in Rails

I have multiple identical collection selects inside a single form. I prefer this over a multiple select list for aesthetic and UX reasons. I have to use a terrible kludge to make everything work right, and I'm wondering if there is a more elegant way to do this:
From the view:
<% 3.times do |i| %>
<%= collection_select("selected_item_" + i.to_s.to_s, :name, #items, :name, :name, { :include_blank => true }, { id: "selected_item_" + i.to_s }) %>
<% end %>
From the controller:
ItemContainer = Struct.new(:name)
3.times do |i|
param = ('selected_item_' + i.to_s).to_sym
instance_variable = '#' + param_name
if params[param] && !params[param].empty?
#selected_items << params[param][:name]
instance_variable_set(instance_variable, ItemContainer.new(params[param][:name]))
end
end
#selected_channels.each.... # do what I need to with these selections
Most of these gymnastics are needed to ensure that the item is still selected if the page is refreshed. If there were some way to force collection select to use arrays, that would be the answer, but I couldn't make that work.
If I understang right you're looking for selegt_tag method (docs: http://api.rubyonrails.org/classes/ActionView/Helpers/FormTagHelper.html#method-i-select_tag)
You can write something like this
select_tag "people[]", options_from_collection_for_select(#people, "id", "name")
select_tag "people[]", options_from_collection_for_select(#people, "id", "name")
and it youd output two selects for people, which would be sent as an array on submit.
if you use the [] naming in your collection_select calls then the params will send over the data as an array
I am a bit confused as to the usage of collection_select here as it doesn't seem like you are using a model object? this example using select_tag - might be able to come up with something more appropriate to your issue if the model structures were known
# run this in the loop
# set selected_value to appropriate value if needed to pre-populate the form
<%= select_tag('name[]',
options_from_collection_for_select(#items, 'name', 'name', selected_value),
{ include_blank: true }
)
%>
in controller update/create action
# this works because the select tag 'name' is named with [] suffix
# but you have to ensure it is set to empty array if none are passed, usually only issue with checkboxes
names = params[:name] || []
names.each do |name|
puts name
end
side note: you can use string interpolation with ruby double quotes in places of + for string concatenation
<%= collection_select("selected_item_#{i}",
:name,
#items,
:name,
:name,
{ include_blank: true },
{ id: "selected_item_#{i}" }
)
%>
see also: http://apidock.com/rails/v3.2.13/ActionView/Helpers/FormOptionsHelper/options_from_collection_for_select

Form for taggable post model

I'm working in a form for post than can have tags. The relationship is a classic has_and_belongs_to_many between Post and Tag.
Problem is I can't initialize a post with an array of tag ids, like this:
Post.new(tags: [1, 2, 3, 4]) # won't wotk. ActiveRecord expects Tag instances
So my current form is like this:
= form_for #post do |f|
= f.text_field :title, placeholder: 'title...'
= f.text_area :body
= fields_for :'post[tags]' do |ft| # hacky. using #post.tags raised 'undefined `model name` for `#post.tags`'
- Post.tags.each do |tag| # I defined Post::tags since I can't Acces Tag class here
= ft.check_box tag.id
= tag.name
= f.submit 'Save'
This form forces me to hack a little in either the controller, but seems like a bad practice. I also thought I could override ActiveRecord::Base initializators to allow ids so my first example works, but surely I'm missing something here.
Try setting tags_singular_ids to your ids. You can check out http://api.rubyonrails.org/classes/ActiveRecord/Associations/ClassMethods.html#method-i-has_and_belongs_to_many for more of the methods that has_and_belongs_to_many provides.
Easy and bit hacky solution:
# defined in my posts controller
def post_params
p = params.require(:post).merge(user: current_user)
p[:tags] = p[:tags].map do |id, value|
value == '1' ? Tag.find(id) : nil
end.compact!
p
end

Passing hash as values in hidden_field_tag

I am trying to pass some filters in my params through a form like so:
hidden_field_tag "filters", params[:filters]
For some reason the params get changed in the next page. For example, if params[:filters] used to be...
"filters"=>{"name_like_any"=>["apple"]} [1]
...it gets changed to...
"filters"=>"{\"name_like_any\"=>[\"apple\"]}" [2]
note the extra quotations and backslashes in [2] when compared to [1].
Any ideas? I'm attempting to use this with searchlogic for some filtering, but I need it to persist when I change change objects in forms. I would prefer not to have to store it in session.
My solution was just to re-create each of param with key-value pair:
<% params[:filters].each do |key,value| %>
<%= hidden_field_tag "filters[#{key}]",value %>
<% end %>
You actually want/need to 'serialize' a hash using hidden fields.
Add this to your ApplicationHelper :
def flatten_hash(hash = params, ancestor_names = [])
flat_hash = {}
hash.each do |k, v|
names = Array.new(ancestor_names)
names << k
if v.is_a?(Hash)
flat_hash.merge!(flatten_hash(v, names))
else
key = flat_hash_key(names)
key += "[]" if v.is_a?(Array)
flat_hash[key] = v
end
end
flat_hash
end
def flat_hash_key(names)
names = Array.new(names)
name = names.shift.to_s.dup
names.each do |n|
name << "[#{n}]"
end
name
end
def hash_as_hidden_fields(hash = params)
hidden_fields = []
flatten_hash(hash).each do |name, value|
value = [value] if !value.is_a?(Array)
value.each do |v|
hidden_fields << hidden_field_tag(name, v.to_s, :id => nil)
end
end
hidden_fields.join("\n")
end
Then, in view:
<%= hash_as_hidden_fields(:filter => params[:filter]) %>
This should do the trick, even if you have a multilevel hash/array in your filters.
Solution taken http://marklunds.com/articles/one/314
I just wrote a gem to do this called HashToHiddenFields.
The core of the gem is this code:
def hash_to_hidden_fields(hash)
query_string = Rack::Utils.build_nested_query(hash)
pairs = query_string.split(Rack::Utils::DEFAULT_SEP)
tags = pairs.map do |pair|
key, value = pair.split('=', 2).map { |str| Rack::Utils.unescape(str) }
hidden_field_tag(key, value)
end
tags.join("\n").html_safe
end
Here's how I managed to pass a parameter value through my view - that is, from View A through View B and on to the controller:
In View A (index):
<%= link_to 'LinkName', {:action => "run_script", :id => object.id} %>
In View B (run_script):
<%= form_tag :action => 'index', :id => #object %>
<%= hidden_field_tag(:param_name, params[:id]) %>
In the controller:
Just reference params[:param_name] to make use of the value.
The key transition that wasn't documented anywhere I could find is where {... :id => object.id} from View A is passed on to View B as <%... :id => #object %>, which View B then passes on to the controller as (:param_name, params[:id]) through the hidden_field_tag construct.
I didn't see this documented anywhere but after perusing several posts across several sites including this post (whose syntax provided the key inspiration), the solution finally gelled. I've seen the caveats on hidden fields pertaining to security but have found no other way to do this given my current design, such as it is.
it's because when you convert in HTML with your hidden_field_tag, the backquote is add. After when you received it like a string not a Hash.
The Hash type can't exist in HTML. You have only string. So if you want pass your hash (not recommend by me), you need eval it when you received it. But can be a big security issue on your application.
As a caveat to Vlad's answer, I had to use raw:
<%= raw hash_as_hidden_fields(:filter => params[:filter]) %>
to get it to work in Rails 3.1.1. Essentially, the text being output was being escaped, eg., "<" becoming "&lt".
Assuming the hash is strings, symbols, numbers, and arrays, you can call eval to convert the params string of the hash from the hidden_fields form back into a hash in the controller. Then the backslash escape characters for the quotes added are no longer an issue:
hash = eval(params["hash_string"].to_s)
Credit to the following article for helping identify this simple solution for my case:
How do I convert a String object into a Hash object?
Keep in mind the contents of the params should be cleaned with .require and .permit.

Resources