Autocomplete with Rails and Prototype
Written by Robin Fisher in 579 words
9
Nov
For an application, I’m working on, I needed to associate an aircraft to an airport. The airport database I’m using has about 10000 entries so using a select box was impractical.
I looked at some autocomplete plugins but none of them seemed to do what I wanted so I decided to make my own. The solution had to take a string entered by the user and return a list of airports containing that string; display the list of airports, then fill the text box when a user clicks on the airport name. This is how I did it:
new Form.Element.Observer('plane_airport_id', 0.1, function(event) {
var string = $F('plane_airport_id');
if (string.length >= 3) {
This sets up an observer on the appropriate form field, captures the value and, if the length of the input is greater than or equal to three characters, executes the next part of the script.
new Ajax.Request('/airports', {method:'get',parameters:{search:string},
onSuccess: function(transport) {
var airports = transport.responseJSON;
We then setup an XHR to the index method of our airports controller, passing the field value as the search parameter. When the call returns successfully, we pass the returned data into the airports variable.
var lis = $('airport-list').descendants();
if (lis.length > 0) {
lis.each(function(l) {
l.remove();
});
};
[javascript]
This removes any existing airports in the airport list.
[javascript]
airports.each(function(a) {
var item = "<li class='airport'><a href='javascript:;' class='airport-link'>" + a + "</a></li>";
$('airport-list').insert(item);
});
$$('.airport-link').each(function(a) {
a.observe("click", function() {
var text = $(this).innerHTML;
Form.Element.setValue('plane_airport_id',text);
});
});
Finally, we take each returned airport, enclose it in list item and anchor tags, and insert each into the unordered list element already in the page. We attach an observer to each anchor tag, so that when it is clicked, it will update the field with the appropriate value. The remaining open brackets are closed accordingly.
The index action in the airports controller is fairly straightforward:
def index
search = params[:search]
airports = Airport.find(:all, :conditions => ['name LIKE ?', "%"+search+"%"])
@airports = Array.new
airports.each do |a|
@airports << "#{capitalize_all(a.name)}, #{capitalize_all(a.country)} (#{a.icao})"
end
render :json => @airports.to_json
end
As the form passes the airport as a string value, when creating an aircraft, I use a regex to extract the ICAO identifier from the string, lookup the corresponding airport and manually update the association column for the aircraft.
The entire javascript code is available as a gist.
