Autocomplete with Combobox

Tom M. Yeh, Potix Corporation
December 13, 2006

Version

Applicable to ZK 2.0 and later.

The Issue

The autocomplete feature is getting popular these days. Here I illustrate how to use the onChanging event to implement it with a combobox.

A Solution

One of the simplest way to implement the autocomplete feature is to listen the onChanging event, and then alter the child components, comboitem, dynamically based on the content that the user is entering.

In the following codes, I assumed we have a dictionary consisting of all possible suggestions that are used to speed up user's entry. In a real application, you might load the suggestions from a database or a Web service.

The Source Codes


public class AutoComplete extends Combobox {
  public AutoComplete() {
    refresh(""); //init the child comboitems
  }
  public AutoComplete(String value) {
    super(value); //it invokes setValue(), which inits the child comboitems
  }

  public void setValue(String value) {
    super.setValue(value);
    refresh(value); //refresh the child comboitems
  }
  /** Listens what an user is entering.
   */
  public void onChanging(InputEvent evt) {
    refresh(evt.getValue());
  }

  /** Refresh comboitem based on the specified value.
   */
  private void refresh(String val) {
    int j = Arrays.binarySearch(_dict, val);
    if (j < 0) j = -j-1;

    Iterator it = getItems().iterator();
    for (int cnt = 10; --cnt >= 0 && j < _dict.length && _dict[j].startsWith(val); ++j) {
      if (it != null && it.hasNext()) {
        ((Comboitem)it.next()).setLabel(_dict[j]);
      } else {
        it = null;
        new Comboitem(_dict[j]).setParent(this);
      }
    }

    while (it != null && it.hasNext()) {
      it.next();
      it.remove();
    }
  }

  private static String[] _dict = { //alphabetic order
    "abacus", "accuracy", "acuity", "adage", "afar", "after", "apple",
    "bible", "bird", "bingle", "blog",
    "cabane", "cape", "cease", "cedar",
    "dacron", "definable", "defacto", "deluxe",
    "each", "eager", "effect", "efficacy",
    "far", "far from",
    "girl", "gigantean", "giant",
    "home", "honest", "huge",
    "information", "inner",
    "jump", "jungle", "jungle fever",
    "kaka", "kale", "kame",
    "lamella", "lane", "lemma",
    "master", "maxima", "music",
    "nerve", "new", "number",
    "omega", "opera",
    "pea", "peace", "peaceful",
    "rock",
    "sound", "spread", "student", "super",
    "tea", "teacher",
    "unit", "universe",
    "vector", "victory",
    "wake", "wee", "weak",
    "xeme",
    "yea", "yellow",
    "zebra", "zk",
  };
}
  

The isChangingBySelectBack Method

ZK 2.2 introduced a new method called isChangingBySelectBack to the org.zkoss.zk.ui.event.InputEvent class. It denotes whether the onChanging event is caused by user's selecting one of the combo items. When implementing autocomplete, it is better not to filter out unmatched suggestions when user presses UP and DOWN keys to select from the list of suggestions. In other words, it is more user friendly if the onChanging method is as follows.


  public void onChanging(InputEvent evt) {
    if (!evt.isChangingBySelectBack())
      refresh(evt.getValue());
  }
  

Summary

As illustrated, it is easy to provide autocomplete with the onChanging event. Though you have to write some codes, the algorithm to generate the proper list of suggestions can be as sophisticated as you want.