hcpeter
hcpeter

Reputation: 624

JSF2 search box

I develop a little app in javaee6 and jsf2. I want a search field with no button (just type and hit enter and give the result). I have a search method in a bean:

public Book searchByTitle(String title) {
    this.book = bookFacade.searchByTitle(title);
    return book;
}

I want to call this method via jsf page (with parameter? Is it possible?), so I try to do this:

    <h:form>
        <h:inputText id="search" value="#{bookBean.searchString}"></h:inputText>
        <h:commandButton value="Search by title" action="#{bookBean.searchByTitle}">
            <f:ajax execute="search" render="output"></f:ajax>
        </h:commandButton>
        <h2><h:outputText id="output" value="#{bookBean.book.title}"></h:outputText>
        </h2>
    </h:form>

But it isn't work. What the proper way to do a search field in a jsf2 xhtml page?

Edit: I tried to call the searchByTitle function with/without parameter.

Thanks in advance!

Upvotes: 2

Views: 15870

Answers (2)

user517491
user517491

Reputation:

Use the following code to your jsf page:

<h:form>
    <h:inputText id="search" value="#{bookBean.searchString}"></h:inputText>
    <h:commandButton value="Search by title" action="#{bookBean.searchByTitle(bookBean.searchString)}">
        <f:ajax execute="search" render="output"></f:ajax>
    </h:commandButton>
    <h2><h:outputText id="output" value="#{bookBean.book.title}"></h:outputText>
    </h2>
</h:form>

And your java code:

// note the return type is String. return type of methods which are to be called from actions of commandlinks or commandbuttons should be string
public String searchByTitle(String title)
{
    this.book = bookFacade.searchByTitle(title);
    return null;
}

EDIT:

First, As far as parameter passing from jsf page is concerned, it should only be used if you want to pass a property of one bean to another bean. In your case, it is not required, since you are setting the property(searchString) of the same bean, and passing it to the very same bean.

Your second question: you want that search works without search button: You can do this by adding a valueChangeListener to the h:inputText. And set the event attribute of f:ajax to blur

<h:form>
    <h:inputText id="search" value="#{bookBean.searchString}" valueChangeListener="#{bookBean.searchStringChanged}">
        <f:ajax execute="search" render="output" event="blur" />
    </h:inputText>
    <h2><h:outputText id="output" value="#{bookBean.book.title}" />
    </h2>
</h:form>

Also define the valueChangeListener method to bookBean java code:

public void searchStringValueChanged(ValueChangeEvent vce)
{
    searchByTitle((String) vce.getNewValue);
}

Upvotes: 1

Arjan Tijms
Arjan Tijms

Reputation: 38163

First step, If you are using a Java EE 6 compatible container (like GlassFish V3, Resin 4, JBoss AS 6 or 7, etc) and you have put something like commons-el.jar in your WEB-INF, remove it. This isn't needed and will only do harm.

I want a search field with no button (just type and hit enter and give the result)

In that case you also don't need the command button, but could go with something like this:

<html xmlns="http://www.w3.org/1999/xhtml"
    xmlns:h="http://java.sun.com/jsf/html"
    xmlns:f="http://java.sun.com/jsf/core"
>
    <h:head/>

    <h:body>        

        <h:form>
            <h:inputText id="search" value="#{bookBean.searchString}" 
                onkeypress="if (event.keyCode == 13) {onchange(event); return false;}"
                onchange="return event.keyCode !== undefined" 
            >
                <f:ajax listener="#{bookBean.updateBook}" render="output" />
            </h:inputText>

            <h2>
                <h:outputText id="output" value="#{bookBean.book}"/>
            </h2>
        </h:form>

    </h:body>
</html>

With the bookBean defined as:

@ViewScoped
@ManagedBean
public class BookBean {

    private Map<String, String> exampleData = new HashMap<String, String>() {{ 
        put("dune", "The Dune Book"); 
        put("lotr", "The Lord of the Rings Book");
    }};

    private String searchString; 
    private String book;

    public void updateBook(AjaxBehaviorEvent event) {
        book = exampleData.get(searchString);
    }

    public String getSearchString() {
        return searchString;
    }

    public void setSearchString(String searchString) {
        this.searchString = searchString;
    }

    public String getBook() {
        return book;
    }

}

In the Facelet, note the importance of the h:head element. Without it JSF does not know where to insert the required script for AJAX support in your response.

In the example, the default onchange method that JSF generates when the f:ajax client behavior is added to so-called EditableValueHolders is called whenever the user presses enter. The listener attribute causes a method to be called on the AJAX event. Since searchString is already bound to a property of the backing bean, you don't have to provide it as argument to the listener method.

A small disadvantage of this method is that the AJAX call will also be invoked when you change the value in the search field and simply click outside it.

Update

Adding an onchange handler to h:inputText fixed the above mentioned problem. This is automatically chained by JSF with the AJAX code from the client behavior. If it returns false, the chain is aborted and thus the AJAX call doesn't take place.

The handler onchange="return event.keyCode !== undefined" will return false if the handler is triggered by the real change event; the event object will then not have the keyCode property.

Upvotes: 6

Related Questions