Friday May 08, 2009

Live Search in JSF

Suppose we have list of things, or a table of things, and a search box. The list should update based on what the user types. Additionally, the search should be live, in that the table updates as the user is typing; they are not required to hit enter, click a button, or otherwise take some action.

For the first attempt at this, let's add a valueChangeListener to your inputText component.

<ice:inputText
  value="#{aBean.searchFilter}"
  valueChangeListener=#{aHandler.search}/>

The problem is that this is not triggered until the form submits. You're going to need to add some Javascript to make this happen. For the next attempt, let's hook some Javascript into the component's keyup event,

<ice:inputText
  value="#{aBean.searchFilter}"
  onkeyup="doSubmit(this)"/>

 and,

function doSubmit(element) {
  iceSubmitPartial(
    document.getElementById("form"),
    element,
    MouseEvent.CLICK
  );
  setFocus(element);
}

Note that I'm using Icefaces, and making use of their utility Javascript function icePartialSubmit() to do a "partial submit". In a nutshell, this submits only this element and ignores the rest of the form. The call to setFocus() avoids losing focus in the input after the submission (another Icefaces utility).

This solution works fine, as long as the user is a slow typer. To understand what's wrong, suppose the user (quickly) types "java".

  1. user types "j"
  2. keyup occurs for "j"
  3. form is submitted
  4. bean is updated with value "j"
  5. user types "a"
  6. response is return from #3
  7. input is updated with value from backing bean: "j"

The response overwrites the "a" that the user just typed. What we want to do is add a delay in the form submission. If the keyup occurs, queue a form submission. If another keyup occurs, unqueue the first form submission and queue another, etc. It's set/cancelTimeout() to the rescure for this,

function doSubmit(element) {
        iceSubmitPartial(
            document.getElementById("form"),
            element,
            MouseEvent.CLICK
        );
        submitTimeout = null;
        setFocus(element);
}

function submitNow(element) {
    if (submitTimeout != null) {
        clearTimeout(submitTimeout);
    }
    submitTimeout = setTimeout(function(){doSubmit(element)}, 1000);
} 

The component binds keyup to submitNow(),

<ice:inputText
  value="#{aBean.searchFilter}"
  valueChangeListener=#{aHandler.search}
  onkeyup="submitNow(this)"/>

We use a delay of 1s. This still isn't perfect, because there's still a chance that the user will type something between a form submission and response. It is however much less likely considering average typing patterns.


About

jtb

Search

Categories
Archives
« April 2014
SunMonTueWedThuFriSat
  
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
   
       
Today