Multiple Values from Scriptaculous' Autocomplete
Saturday, November 11, 2006 at 04:48PM
Wayne Robinson in javascript, programming, ruby on rails

Ever wanted to extract multiple values from a Script.aculo.us Google Suggest-like autocomplete text field? I recently did and here's how.

If you aren't aware of how to use autocomplete text fields, please read over the simple and customised demos available at Script.aculo.us. Also, a warning, this demo utilises Ruby on Rails however, with a little bit of modification, this should work using the Script.aculo.us library without Ruby on Rails.

This example will auto-populate a state and postcode based on the user's selected suburb (yes, I'm Australian).

The first step is to create a view for the page containing the autocomplete field and for the autocomplete field itself.

Controller:

def new
# This is the main controller that will
# contain the autocomplete field
@contact = Contact.new
end

def auto_complete_for_suburb
suburb = params[:suburb]
@surburbs = Suburb.find_by_name(suburb,
:order => "name", :limit => 20)
render :partial => "auto_complete_suburb"
end

View for the new action (assume an application.rhtml template has been created, this template must include the Prototype and Script.aculo.us javascript libraries):

<%= form_tag({:action => :create}, {:method => :post}) %>
<table>
<tr>
<th>Name:</th>
<td><%= text_field(:contact, :name) %></td>
</tr>
<tr>
<th>Suburb:</th>
<td><%= text_field_with_autcomplete(:contact, :suburb,
:select => "value",
:after_update_element =>
"function (ele, value) {
$("contact_state").value =
Ajax.Autocompleter.extract_value(value,
'STATE');
$("contact_postcode").value =
Ajax.Autocompleter.extract_value(value,
'POSTCODE'); }
") %>
</td>
</tr>
<tr>
<th>State:</th>
<td><%= text_field(:contact, :state, :size => 10) %></td>
</tr>
<tr>
<th>Postcode:</th>
<td><%= text_field(:contact, :postcode,
:size => 10) %></td>
</tr>
</table>
<%= end_form_tag %>

Before we continue any further, it is worth-while defining the _auto_complete_suburb.rhtml partial.

<ul class="suburbs">
<% unless @suburbs.nil? -%>
<% @suburbs.each do | suburb | -%>
<li>
<%= h("#{suburb[:name]}, #{suburb[:state]}" +
"#{suburb[:postcode]}") %>
<div class="value" style="display: none;">
<%= h(suburb[:name]) %>
</div>
<div class="STATE" style="display: none;">
<%= h(suburb[:state]) %>
</div>
<div class="POSTCODE" style="display: none;">
<%= h(suburb[:postcode]) %>
</div>
</li>
<% end -%>
<% end -%>
</ul>

The autocompleter view has three (3) hidden <div> tags which contain the extra data used by the base Autocompleter Javascript methods as well as the new one (extract_value) that will be defined below. 

You may also want to cast your eye over the suburb field definition in the new contact view as this is where most of the action is. There are two options to note:

The Script.aculo.us Autocompleter methods conviently pass the complete contents of the <li> field to the method specified in the :after_update_element option. This allows us to extract any additional values from this data. I have created a simple addition to the Ajax.Autocompleter class below that speeds this extraction:

Additional method for Ajax.Autocompleter class. This can be declared anywhere after the inital Script.aculo.us script inclusion. For my purposes I put this at the top of my application.js file. 

Ajax.Autocompleter.extract_value = 
function (value, className) {
var result;

var elements =
document.getElementsByClassName(className, value);
if (elements && elements.length == 1) {
result = elements[0].innerHTML.unescapeHTML();
}

return result;
};

 So that's all there is to it. If anyone would like me to create a demo of the above code, ask me and, if I get enough requests, I'll put something together.

Article originally appeared on Wayne Robinson's Blog (http://wayne-robinson.com/).
See website for complete article licensing information.