Thursday, 13 November 2008

grails + ajax: autocomplete

Having an autocomplete feature on a textfield is a good way of improving user experience. As the user types a string, the textfield tries to anticipate what the user is typing and offers possible matches. Clicking on a match results in autopopulation of the textfield thereby saving time for the user.

In this post I describe a way of implementing this feature using grails and scriptaculous. To help explain the method, I will develop a simple application where we have a form containing a text field for countries of the world. As a user begins to enter the name of the country, the textfield presents a list of possible matches. Finding the country he wants, the user clicks on the entry and the textfield is immediately populated with the selection [see picture below].




















Before ploughing into the specific details of how to implement this feature, it's useful to gain a high level view of what is going on "under the hood".


To begin with, the textfield needs to be registered with an ajax function [defined within scriptaculous]. This function will detect keystrokes and then call an appropriate action within a grails controller. The action in turn will employ GORM to perform a hibernate search for records that match the pattern of text the user has just entered. The results from this search will be returned to the view and rendered as a list just under the text box.

So to build your own autocomplete search text field, you need to do the following,

Step 1: Import in scriptaculous

Copy and paste the following into the header element of your gsp page


<g:javascript library='scriptaculous'/>



Step 2: create the form with the text field and the supporting div

Within the gsp page create a text field and a supporting div to display the results of the autocomplete feature.


<g:form name="find"..... >

..............
..............

<%-- Text field supporting autocomplete --%>
<input type="text" id="autocomplete" name="autocomplete_parameter"/>


..........
..........

</g:form>

<%-- supporting div to display potential matches.--%>

<div id="autocomplete_choices" class="autocomplete" ></div>




Step 3: Implement grails action to respond to ajax call

In this section we create the action in a grails controller that will respond to the ajax function.

def ajaxAutoComplete = {

if(params.autocomplete_parameter)
{
def input = params.autocomplete_parameter + '%'

def list = Country.findAll("from Country as country
where lower(country.name) like :name", [name:input])

StringBuffer idList = new StringBuffer()

idList.append("<ul>")

list?.each{c -> idList.append("<li>" + c.name+"</li>")}

idList.append("</ul>")

render idList.toString()
}
}



The action receives a variable called autocomplete_parameter containing the keystrokes entered by the user. This string is used in a hibernate query to retrieve the set of countries with matching names. Finally the search results are embedded within a html list that is subsequently rendered to view.

Step 4:
Code up the ajax call

In this case, we're simply setting up and calling an ajax function [Ajax.Autocompleter] from scriptaculous.


<html>
<head>
<meta equiv="Content-Type" content="text/html; charset=UTF-8">
<meta name="layout" content="main">

<g:javascript library="'scriptaculous'/">

<g:javascript>
window.onload = function()
{
new Ajax.Autocompleter("autocomplete",
"autocomplete_choices",
"/autocomplete/country/ajaxAutoComplete",
{}
);
}
</g:javascript>





The first argument to this function is the id of the UI element we're monitoring, i.e. the textfield. Next we pass in the name of the element that will be updated with the results of the ajax call. Thirdly we pass in the url to the grails code that will service the ajax function. This url is of the form /web context/controller/action.

Step 5: Add styling to the supporting div.

Place the following excerpt into /web-app/css/main.css


div.autocomplete {
position:absolute;
width:250px;
background-color:#8cafea;
margin:0px;
padding:0px;
overflow:hidden;
border: 1px solid;
}

div.autocomplete ul {
list-style-type:none;
margin:0px;
padding:0px;
overflow:auto;
}

div.autocomplete ul li.selected { background-color: #294597; color: #ffffff}

div.autocomplete ul li {
list-style-type:none;
display:block;
margin:0 0 0 5;
padding:2px;
height:12px;
cursor:pointer;
}


Presto !!! We now have an autocomplete solution using grails + scriptaculous. This implementation could be improved further by limiting the number of matches found by hibernate to some number say 8. This would prevent situations whereby the user is inundated with a long list of possible matches. There are alternative ways to implement this feature. These include using the RichUI or GrailsUI plugins.

You can download this example [autocomplete.zip] here. This example was built using grails 1.03

2 comments:

Adenny said...

hi, thx for the post. this works but what i need is to get the id of the autocomplete_parameter to be passed on to the next gsp. how do i go about doing that? thx.

maxarbos said...

I would like to use this to search different columns (or sets of data), how would I change the 'parameters' option of the Ajax.Autocomplete object?

I am trying an onchange function to set the parameter, and it shows the right value in an test alert box, but then only the onriginal onload value is ever used for the actual search. Any suggestions would be great. thanks.