Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Rails: multiselect filtering with a blank option

This is a problem I'm coming across regularly. Let me explain it by a simplified example:

Say I want to show a search page where results can be filtered by selecting one or multiple product categories. In the view, this looks like:

<%= select_tag("product_categories", options_from_collection_for_select(@product_categories, 'id', 'name'), multiple:true, include_blank:"(all categories)" %>

Now, in the controller, I have something like:

@filtered_products = Product.all

...

if params[:product_categories].present? 
   @filtered_products = @filtered_products.where(category_id: params[:product_categories].map(&:to_i))
end

...
#etc

However, as it is impossible to deselect a multiselect when it has clicked, there is a blank option. But, when this option is set, params[:product_categories] contains [""]. This results in the if-statement to evaluate, and as "".to_i == 0, we only get products with category 0 (which usually is none, as ActiveRecord starts ids from 1 in the database). This is not the desired result, as in fact we want all products when the blank option is selected.

Handling this case is made even more difficult because, it is possible to accidentally select both the blank option and one or multiple other options. So this case needs to be handled as well.

I have changed the if-statement to

if params[:product_categories].present? && params[:product_categories].any? && (params[:product_categories].length > 1 || params[:product_categories].first != "")
   ...
end

It works, but this code is very ugly. I am wondering if there is a nicer, more DRY, Rails-like way to do this.

like image 326
Qqwy Avatar asked Sep 17 '15 08:09

Qqwy


3 Answers

When you have no one categories selected you can add hidden_field same as product_categories to avoid [""] with nil value before your select options.

<%= hidden_field_tag "product_categories" %>
<%= select_tag("product_categories", options_from_collection_for_select(@product_categories, 'id', 'name'), multiple:true, include_blank:"(all categories)" %>

Then to handle it. It does not need to map(&:id) because "" will be generated into 0 automatically in query.

if params[:product_categories] != [""]
   @filtered_products = @filtered_products.where(category_id: params[:product_categories])
end

These is why embedded array always showing in multiple select options. I hope this make clean your code.

like image 166
akbarbin Avatar answered Nov 03 '22 06:11

akbarbin


Try

if params[:product_categories].reject!(&:empty?).any?

end
like image 30
Florin Ionita Avatar answered Nov 03 '22 07:11

Florin Ionita


Just improving Florin's answer a bit.

params[:product_categories].reject!(&:empty?).any?  if params[:product_categories].length > 1     
like image 32
Mahesh Avatar answered Nov 03 '22 08:11

Mahesh