Tuesday October 30, 2007
Creating autocomplete entry field with Woodstock
Overview
Woodstock components (https://woodstock.dev.java.net/index.html) provide powerful and flexible set of client side Web components. Like living cells, not only components represent complete functional units by themselves, they can also be connected together to create new forms of functionality.
This document shows how a textField and listBox can be connected together to build a so-called autocomplete entry field – the one that presents to the user dynamic set of completing entered data or suggestions based on what information user is typing.
All sources and Netbeans ( NB 6 Beta 2) project are attached at the end of this document.
For this exercise, we will build an autocomplete entry field that is drawing data from the list of New York Times' “The Best 1,000 Movies Ever Made” that will look like this:

The
intended behavior is to change the content of the list box to present
only titles that contain typed in string.
Steps:
Think through the intended interactions
Design the entry field by placing textField and listBox next to each other
Code the back-end
Connect textField and listBox
Think through the intended interactions
The following diagram illustrates the simplified interactions/dataflow that we want to accomplish:

When user types into the textField, we want listBox to be refreshed with relevant filtered data. We want server to perform some data processing for that as well.
When user selects data in the listbox, we want this selection to be reflected in the textField so it can be used for submission
Design the entry field
We used Netbeans 6 to place textField and listBox next to each other ( see Page1.jsp):
Note that when grid placement is used on the Netbeans design surface, it automatically creates a space between any two components, so we modified styles “top” position of the list box to 'attach' it to the textField.
<webuijsf:textField
binding="#{Page1.tf}"
columns="60"
id="tf"
style="position:
absolute; left: 144px; top: 93px; width: 120px; height: 24px"
text="#{Page1.filter}"/>
<webuijsf:listbox
binding="#{Page1.listbox1}" id="listbox1"
items="#{Page1.listbox1DefaultOptions.options}"
rows="10"
style="position: absolute; left: 144px; top: 112px; width:
120px; height: 48px"/>
Code the back-end
When components are placed on the design surface, Netbeans automatically creates JSF server binding for the component. By default, it uses DefaultOptionsList as a data binding for the listBox. DefaultOptionsList provides simple data set, and we will be replacing it with custom class FilteredOptions.
Responsibilities of FilteredOptions class are:
load data from persistence ( in this example, we load data from bundle movies.properties)
filter data according to the filter provided
sort data
provide data in suitable to listBox form of Option[]
See FilteredOptions.java for details. Note that we isolated filtering of the data from getOptions(). The reason is that during miscellaneous JSF lifecycles method getOptions() may be called several times for the same request. Since filtering data can potentially be a resource-intensive procedure, it would be undesirable to perform it when unnecessary. Thus data filtering is done once(when committing data from textField) in Page1.java :
public
void setFilter(String value) {
this.filter
= value;
listbox1DefaultOptions.filter(filter);
}
Connect textField and listBox
And finally we need to wire together the communication on the client side ( browser)
First, we will make keyUp event of the text field to commit textField data and update the listbox:
<webuijsf:textField
binding="#{Page1.tf}"
onKeyUp="document.getElementById('form1:listbox1').refresh('form1:tf');
"
columns="60"
id="tf"
style="position:
absolute; left: 144px; top: 93px; width: 120px; height: 24px"
text="#{Page1.filter}"/>
Note that refresh(id1, id2,..) function does 2 things:
it submits ( by Ajax call) fields with id1, id2. In our case we supply id of the textField, which means that textField value will be submitted first.
It refreshes ( by Ajax call) the field upon which it was called. In our case document.getElementById('form1:listbox1') represents the listBox.
That is it for this part. Now any character typed will cause TextField to submit and ListBox to refresh from the server data!
Next, we want textField to be updated with the full title when the title is clicked upon within the lisbox.
<webuijsf:listbox
binding="#{Page1.listbox1}" id="listbox1"
items="#{Page1.listbox1DefaultOptions.options}"
onChange="document.getElementById('form1:tf').setProps(
{value:
document.getElementById('form1:listbox1').getSelectedValue() } ); "
rows="10"
style="position: absolute; left: 144px; top: 112px; width:
120px; height: 48px"/>
In the above javascript, we are using 2 functions :
setProps() - this function is common across all of the client side components, and is to be used to set 1 or more properties on the component. In this case, we are setting value of the textField
getSelectedValue() - function exposed by listBox ( see TLD documentation)
Resources
Complete
Netbeans 6 Beta 2 project
Complete
Netbeans 6 Beta 2 project for Woodstock 4.1.1 Make sure to compile the project
before opening pages in the NB visual designer