How do I search for multiple records in a search form? - ruby-on-rails

I am trying to allow the user to be able to choose multiple records in a field on the search form.
Something like this:
<%= f.input_field :neighborhood_id, collection: Neighborhood.order(:name), :url => autocomplete_neighborhood_name_searches_path, :as => :autocomplete, 'data-delimiter' => ',', :multiple => true, :class => "span8" %>
It sends it to my search model like this: #search = Search.create!(params[:search])
This is what the Search.rb model does with it:
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?
listings
The issue is that this is just accepting 1 neighborhood_id, so I am getting this error when I choose multiple objects:
undefined method `to_i' for ["Alley Park, Madison"]:Array
Where Alley Park and Madison are the names of 2 neighborhoods, not the IDs.
So how do I get this working?
Thanks.
Edit 1
The issue seems to not be in the lookup of the params[:search] per se, but rather in the conversion of the form input to an array of entries. I tried changing the search method to be something like:
listings = listings.includes(:neighborhood).where("neighborhoods.name like ?", neighborhood_id) if neighborhood_id.present?
Don't get hung up on the fact that I am looking up neighborhood.name and passing in neighborhood_id. I just did that because I know that the params for the field neighborhood_id were actually the names of the neighborhood. If this had worked, I would have refactored some stuff, but it didn't. So don't get hung up on that.
But that still returns the error undefined method 'to_i'....
Also, I still get that error even if I just pass in 1 option.

listings = listings.where("neighborhood_id in (?) ", neighborhood_id)

You can get the id instead of neighborhood names from the input field like this:
<%= f.input_field :neighborhood_id, collection: Neighborhood.order(:name), :url => autocomplete_neighborhood_name_searches_path, :as => :autocomplete, 'data-delimiter' => ',', :multiple => true, :class => "span8", :input_html => { :id => "neighborhood_id" } %>

Related

NoMethodError when returning an array in a form

I'm building an app for a restaurant and I have a form where I add meals to an order and a price field gets dynamically updated depending on what dishes and how many of them you pick.
To do that I built a nested form (I think that doesn't matter anyway) which looks as follows:
.nested-fields
= f.collection_select(0, #dishes.collect{ |dish| [dish.name, :data => {:description => dish.price}]}, :name, :name, {include_blank: true}, {class: "meal-select"})
= f.select :quantity, options_for_select((1..10))
= f.text_field(:price, disabled: true)
= link_to_remove_association "X", f
The thing that bugs me is the collection_select. As you can see, I am returning an array with a name and a data-description which goes to the HTML tag. Based on the data-description, my price field gets updated.
However, I have no idea what method I should choose to extract the name of a dish. As you can see I tried 0 since name of the dish is always first in the array. I have also tried :first, :name but none of those works! The error I get is:
"NoMethodError in Orders#new
undefined method '0' for #Meal:0x007fe4eb8e26c8"
or when I use :name
undefined method `name' for ["Zupa z Krewetkami", {:data=>
{:description=>17.0}}]:Array
Naturally, it points to:
= f.collection_select(0, #dishes.collect{ |dish| [dish.name, :data => {:description => dish.price}]}, :name, :name, {include_blank: true}, {class: "meal-select"})
I don't think the problem lies in my controller but, I'll show it just in case:
def new
#dishes = Dish.all
#order = current_user.orders.build
end
I tried looking for an answer here but as you can see the problem has not been solved and it was slightly different than mine.
To sum up - my question is what method I should use to extract name of the dish from my array in collection_select. Thanks!
Here is how you can use collection_select
...
= f.collection_select :meal_select, #dishes, :name, :price, {include_blank: true}, {class: "meal-select"}
...
For more details see the docs.
Use below approach
options_for_select( [['First', 1, {:'data-price' => 20}],
['Second', 2, {:'data-price' => 30}]] )
= f.select :meal_select, options_for_select(#dishes.collect{ |dish| [dish.name, dish.price,{'data-description' => dish.price}]}), :class => 'meal-select'

get selected items from select_tag

I have this line in my rails app:
<%= select_tag :questionnaire_id,
options_for_select(#questionnaires_types, #questionnaires_ids),
:multiple => true, :size => 7 %>
which works fine.
but when I try to use the multiple values that were selected I get this:
questionnaire_id"=>["1687,1688,1689,1690,1691,1724"]
instead of this:
questionnaire_id"=>["1687", "1688", "1689" ,"1690", "1691", "1724"]
i.e. I get 1 item instead of 6 items.
any suggestions?
According to rails code: https://github.com/rails/rails/blob/41231ef6c6c6a6e546b69add28f04aafb9e0e952/actionview/lib/action_view/helpers/form_tag_helper.rb#L134
The name must end with [] to be make sure you receive an array.
def select_tag(name, option_tags = nil, options = {})
option_tags ||= ""
html_name = (options[:multiple] == true && !name.to_s.ends_with?("[]")) ? "#{name}[]" : name
if options.delete(:include_blank)
option_tags = content_tag(:option, '', :value => '').safe_concat(option_tags)
end
if prompt = options.delete(:prompt)
option_tags = content_tag(:option, prompt, :value => '').safe_concat(option_tags)
end
content_tag :select, option_tags, { "name" => html_name, "id" => sanitize_to_id(name) }.update(options.stringify_keys)
end
So just change it to questionnaire_ids[]
Hope that helps.
I think a collection_select would look nice but I cannot help with that since you did not post anything about the model. Maybe try this so that it knows it is a collection:
<%= select_tag "questionnaire_ids[]", options_for_select(#questionnaires_types, #questionnaires_ids), :multiple => true, :size => 7 %>
Or you could just parse the string you currently receive using #split.
Otherwise post a bit more code about the associations between Questionnaire and what ever this model is.
Well, just in case that someone will come to this issue, I found the problem.
It seems to be a bug in rails.
I was using remote_form_for, and that gave me the strange behaviour. I tried to change the form to form_for instead, and I got an array with 6 items.
Rails, Rails, when will you be like .Net? :-(

Rails: Setting class and data-tag of an HTML attribute with a single rails method

I'm currently working on a tour interface that guides new users around my site. I have a Tour model that has many TourStops, each of which contains information about a section of the site.
Basically, I'd like to write a function for the Tour model that -- when passed the number of a TourStop -- generates the correct class and data attribute for the HTML element it's attatched to. For example, I'd like
<%= link_to image_tag("new_button.png", tour.stop_data(1), :title => 'Add new asset'), new_asset_path %>
to call a function and return something like
def stop_data(order)
" :class => '#{tour_stops.find_by_order(order).name}',
:data => '{:order => order}'"
end
creating a link_to tag like:
<%= link_to image_tag("new_button.png", :class => 'tour_stop_1',
:data => {:order => 1}, :title => 'Add new asset'), new_asset_path %>
The above code doesn't work. Is something like this even possible? If not, what's a better approach I might take?
The image_tag accepts two parameters. A source, and a options Hash.
What you are trying to do is squeezing your return value from stop_data into this options Hash.
In order to get this to work, you first, need to return a Hash from stop_data, and second, make sure you pass only two arguments to image_tag - the source, and the options.
First:
def stop_data(order)
{
:class => tour_stops.find_by_order(order).name,
:data => { :order => order } # you may need order.to_json
}
end
Second:
link_to image_tag("new_button.png", tour.stop_data(1), :title => "Add new asset"), new_asset_path
This looks like it will work, but it won't, since your'e passing three parameters to image_tag.
When you do the following:
image_tag("new_button.png", :class => "tour_stop_1", :data => { :order => 1 }, :title => "Add new asset")
It looks like you're passing even 4 parameters to image_tag, but in fact they are only two. In Ruby, when the last parameter of a method is a Hash, you don't need to wrap the Hash key/value pairs in curly braces ({}), so the example above is essentially the same as
image_tag("new_button.png", { :class => "tour_stop_1", :data => { :order => 1 }, :title => "Add new asset" })
Now, to get your helper to work with image_tag, you need to merge the options, so they become only one Hash.
link_to image_tag("new_button.png", tour.stop_data(1).merge(:title => "Add new asset")), new_asset_path
Again, we're omitting the curly braces when calling merge, because it's only (and therefore last) parameter is a Hash. The outcome is the same as:
tour.stop_data(1).merge({ :title => "Add new asset" })

rails select, with restricted query

Currently I am trying to restrict the information feed into an option select field to only display the criteria I have selected. With the code below this seems to be working
= select("schedule", :selected_players, User.where(:team_id => current_user[:team_id]) { |p| [full_name(p), p.id] }, {:include_blank => 'None', :prompt => 'Add Players to Lineup'}, :multiple => "multiple")
The issue is that this code is displaying an array field type i.e #<User:0xa559830>.
How do I get it to display the actual users name?
I suggest rewriting the code with greater separation and clarity instead of trying to fit it all in one line. Model methods that generate SQL should never be called from views.
In the controller:
#players = User.where(:team_id => current_user[:team_id]).all
In the view:
= select :schedule, :selected_players, #players.map {|p| [full_name(p), p.id] }, {:include_blank => true}...
I think the original error in your code appears to be that you were calling a block after the where method without an iterator (each).

Why is thinking sphinx restricting filtering to one attribute at a time in ruby on rails?

Am I doing something wrong?
If I don't have the with options added location works and I get results shown as soon as I enter the browser page of my website.
When i hit the page all users are shown by 20 per page. If I add with_all gender => params[:gender], location still works and I type in a location and filter results by gender and results are successfully returned.
If I add ethnicity to the with_all hash then ethnicity works and results are turned but gender and location no longer work.
It's like it only allows 1 attribute for filtering.
I have rebuilt the index several times so I don't get what's going on.
I've got text search for location and 2 filters set 1. gender, 2. ethnicity
Here is my Profile model for the profiles table that stores all the attributes above:
define_index do
indexes location
has ethnicity, :type => :integer
has gender, :type => :integer
end
Here is my controller:
class BrowsersController < ApplicationController
def index
#default_image = "/assets/default_avatar.jpg"
#gender = params[:gender].to_i
#users = Profile.search params[:location],
:page => params[:page],
:per_page => 20,
:with_all =>
{
:gender => params[:gender],
:ethnicity => params[:ethnicity]
}
end
end
my view form:
<%= form_tag browsers_path, :method => 'get' do %>
<p>
Location: <%= text_field_tag :location, params[:location] %><br />
Gender: <%= select_tag :gender,
options_for_select([["Select", nil],
["Male", 1],
["Female", 2]], params[:gender]) %>
<br />
Ethnicity: <%= select_tag :ethnicity,
options_for_select([["Select", nil],['Black', 1 ],['White / Caucasian', 2 ],['European', 3 ],['Asian', 4 ],['Indian', 5 ],['Middle Eastern', 6 ],['Native American', 7 ],['Hispanic', 8 ],['Mixed Race', 9 ],['Other Ethnicity', 10 ]], params[:ethnicity]) %>
<br />
<%= submit_tag "Search", :name => nil %>
</p>
<% end %>
There's a lot to digest in your question, but here's a few things to note - perhaps they will help:
:with_all is for matching multiple values in a single multi-value attribute - for example, matching an article that has all three tag ids would use this: :with_all => {:tag_ids => [1, 2, 3]}.
:with, however, is perfectly fine for having filters on more than one attribute - which is what you seem to be after (although :with_all with single filter values behaves in just the same way).
Sphinx treats nils/NULLs as 0's - so, if you're filtering by a gender but not an ethnicity, then what your controller code is doing is searching for profiles with the given gender and an ethnicity of 0. Perhaps try something like this instead:
filters = {}
filters[:gender] = params[:gender].to_i if params[:gender].present?
filters[:ethnicity] = params[:ethnicity].to_i if params[:ethnicity].present?
#users = Profile.search params[:location],
:page => params[:page],
:per_page => 20,
:with => filters
Finally - the gender and ethnicity columns are integers, yes? If so, you don't need to specify :type => :integer in your index definition - that'll be done automatically.

Resources