Rails: Easily add autocomplete to forms
Intro
Since resources in my rails app can easily reach several thousand and million entries, I wanted a way to easily select one in a form. A quick google search led me to this excellent post by releu: http://gistflow.com/posts/428-autocomplete-with-rails-and-select2
While it was great some parts where missing for me, or had to do some additional effort. So I want to provide a 0-100 process to get it working.
Setup
Note: I am using Rails 4 with Bootstrap 3.
Releu recommended the awesome jQuery-based replacement for select boxes select2. Let’s install it. Modify your Gemfile to include the following:
# Gemfile gem 'select2-rails' # later we will take advantage of this gem gem 'kaminari'
Kaminari is a paginator gem for rails.
Now edit the application.js
and application.css.scss
to include select2:
/* application.js */ //= require select2
/* application.css.scss */ *= require select2
Usage
In your view, add a hidden field to the form:
= hidden_field_tag(:resource, '', id: 'res_select', class: 'select2 ajax', style: 'width: 100%;', data: { source: search_resource_names_path, placeholder: 'Search for a name' }) # or = f.hidden_field :organization_id_eq, class: 'select2 ajax', data: { source: organizations_path }
The important parts are
- It is hidden because select2 will create it’s own structure. So if you make it a visible field, it will look probably just ugly.
- The classes
select2
andajax
are required. - Make sure to create the source path in your
config/routes.rb
. This is where the ajax calls will be made to. - I added
style: 'width: 100%;'
, because you cannot use Bootstrap’sform-control
class. That is, because select2 copies all CSS classes and styles to a containerdiv
and not the resulting input field.
To enable select2, the .select2(options)
method has to be called on such elements. The following was adapted from releu:
# your controller.js.coffee $(document).ready -> $('.select2').each (i, e) => select = $(e) options = placeholder: select.data('placeholder') if select.hasClass('ajax') # only add ajax functionality if this class is present options.ajax = url: select.data('source') dataType: 'json' data: (term, page) -> q: term page: page per: 25 results: (data, page) -> results: data.resources more: data.total > (page * 25) # adding the more: option enables infinite scrolling (select2 will load more content if available) options.dropdownCssClass = "bigdrop" select.select2(options)
Last but not least, implement the controller method as pointed to in your routes file:
def search_for_name @resources = Resource.select([:id, :name]). where("name like :q", q: "%#{params[:q]}%"). order('name').page(params[:page]).per(params[:per]) # this is why we need kaminari. of course you could also use limit().offset() instead # also add the total count to enable infinite scrolling resources_count = Resource.select([:id, :name]). where("name like :q", q: "%#{params[:q]}%").count respond_to do |format| format.json { render json: {total: resources_count, resources: @resources.map { |e| {id: e.id, text: "#{e.name} (#{e.username})"} }} } end end
That’s it.
I try to follow this tutorial, but for me it doesn’t work: nothing appear and I have only an hidden field.
I’m not using ‘select2-rails’ gem because I load Select2 and custom CSS from a Bootstrap Theme (Metronics)
Hm, that sounds like the JavaScript did not do anything then. Does your console show errors?
Also, for trying, you can use the JS console to add select2 to your hidden field live on page.
Great walkthrough, cheers! How did you deal with displaying the value (or rather, the text from the object with the ID matching the value…) when you reopened/redisplayed the form?
With it being a hidden field you can only pass the ID into it, so is it a case of doing an AJAX call within initSelection?
Nevermind, got it working in the way described :)
https://gist.github.com/anonymous/f711330df558adc0ce7c
Thanks again
Trying to follow this tutorial also but nothing appears and I just have a hidden field, my javascript console gives the error message
‘Uncaught TypeError: undefined is not a function’
I’m not sure exactly why this is?
This usually occurs when some variable is not correctly initialized / some library is missing / there’s a typo.
When you click on the error it should show you the line where the error occurred. That will help in identifying the cause.
Hello,
This tutorial is exactly what I am looking for. However, I don’t understand this line
“Make sure to create the source path in your config/routes.rb. This is where the ajax calls will be made to.”
Could you give me an example of the line I have to add in my route.rb file please?
So let’s say you are using the tag form for building your form:
Here you have a
data.source
set tosearch_resource_names_path
. This path has to be defined in yourroutes.rb
assearch_resource_names
.E.g.
A full example of a
routes.rb
can be found here: https://github.com/luksurious/fbwatch-ruby/blob/master/config/routes.rbThe repository contains a working autocomplete setup as well.
Hi,
can this also create tags or only find and select?
Thanks!
Great tutorial, works nicely!
Quick update for anyone trying this. If nothing is showing up and console is showing a JS error, try the following:
In the application.js file
Instead of:
//= require select2
Add:
//= require select2-full
Hey luksurious i appreciate your post. I just tried and its working fine, thanks a lot. but one issue is at my end that i want multi select in my element. How can i?
= hidden_field_tag(:icd_procedures_ids, ”, id: ‘res_select’, class: ‘select2 ajax’, style: ‘width: 100%;’, data: { source: fetch_icd_procedures_admin_patient_prescriptions_path, placeholder: ‘Search for a name’ })
Hey Talal, great to see that it is still useful in 2019.
I haven’t used it in a long time and didn’t try this but generally, using the attribute
multiple
on the HTML field should make it a multi-select field.I don’t remember if Rails allows any arbitrary tag or if you need to declare it somehow differently, but you could try this:
Hey, i’ve tried your tutorial cause i really need to make it works for one of my projects, but it doesn’t work :(
I think i surely misunderstood something, so i made a stackoverflow answer about it, if you can help me to figure out what is it :) Have a good day
(https://stackoverflow.com/questions/59577053/rails-autocomplete-with-select2-and-ajax-doesnt-pop-any-data)
Hi,
I’m sorry that it doesn’t work for you. Be aware that the versions of the libraries back in 2013 were quite different than they are now. E.g. select2 has a new major version which I do not know if it works in the same way. The same might apply to Rails and Bootstrap.
From your Stackoverflow question I could see that the indentation in the coffee script is a bit messed up, so make sure that it looks as shown above. Otherwise, be sure to check the console output in your browser, as it will most likely give you hints as to what is not working (right click -> inspect then console tab)