a4j:jsFunction and "row is unavailable" problem
niss Oct 20, 2011 3:21 AMHello everybody,
I have a problem when I use a4j:jsFunction and a datatable.
Functionaly
I have a page with an input field and a search link. You must press [Enter] to submit the form. The search results are displayed just below the input field. When something is entered into this field, a message should appear saying that the input field has been modified and that the displayed results do not match anymore against the search criteria.
The problem
When you quickly modify the input field content and hit [Enter], sometimes, a NullPointerException happens. I guess it because the Ajax call is not completely finished and is maybe modifying my bean.
It works great when I'm waiting a few seconds.
I think this is the most relevant part of the stacktrace :
java.lang.IllegalArgumentException: row is unavailable
at javax.faces.model.ListDataModel.getRowData(ListDataModel.java:66)
at org.ajax4jsf.model.SequenceDataModel.getRowData(SequenceDataModel.java:147)
at org.ajax4jsf.component.UIDataAdaptor.getRowData(UIDataAdaptor.java:242)
at be.cm.apps.prest.beans.prestations.SearchPrestationBean.getCurrentInteger(SearchPrestationBean.java:111)
To get more into the details :
The input field has an onkeyup event attached to it, calling a 'pure' Javascript function called 'addSynchronizationErrorMessage()'. This function is itself calling another Javascript function called 'addSynchronizationErrorMessageAjaxCall()'. The latter is not something I wrote myself : it is a Javascript function generated by Richfaces a4j:jsFunction tag.
Below you'll find the codes. I tried to break it down to the minimal code so that it is easy to reproduce the problem. I've read things I could do to eliminate the problem (ignoreDupResponse, limitToList, a4j:form, ...) but nothing worked so far
We are using the following libraries : MyFaces 1.1.5, Tomahawk 1.1.9, jQuery 1.5.2, RichFaces 3.1.6.
This is the .jsp code :
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <%@ taglib uri="http://java.sun.com/jsf/html" prefix="h"%> <%@ taglib uri="http://java.sun.com/jsf/core" prefix="f"%> <%@ taglib uri="http://richfaces.org/rich" prefix="rich"%> <%@ taglib uri="http://richfaces.org/a4j" prefix="a4j"%> <%@ taglib uri="http://myfaces.apache.org/tomahawk" prefix="t"%> <f:view> <f:loadBundle var="msg" basename="be.apps.bundle.messages"/> <html> <body onkeydown="return search(event)" onload="setFocusOnLoadInScreen();"> <h:form id="actionForm0"> <t:div id="messagesPanel"> <t:messages id="errorMessage" globalOnly="false" showDetail="false" forceIdIndex="true"/> </t:div> <a4j:jsFunction name="addSynchronizationErrorMessageAjaxCall" action="#{searchPrestationBean.addDivergenceBetweenSearchCriteriaAndResultsMessage}" reRender="actionForm0:messagesPanel" immediate="true" /> <h:inputText id="origin" value="#{searchPrestationBean.origin}" onkeyup="addSynchronizationErrorMessage();" /> <t:commandLink id="defaultButton" value="#{msg.global_search}" action="#{searchPrestationBean.search}" /> <rich:dataTable id="dataTableInfoPanels" value="#{searchPrestationBean.integers}" binding="#{searchPrestationBean.dataTable}"> <rich:column> <t:htmlTag value="nobr"> <h:outputText value="#{searchPrestationBean.displayInfo}" /> </t:htmlTag> </rich:column> </rich:dataTable> </h:form> </body> </html> </f:view>
And this is the bean code :
import java.util.Arrays; import java.util.List; import java.util.Random; import org.richfaces.component.html.HtmlDataTable; import be.cm.apps.prest.faces.beans.AbstractScreenBean; public class SearchPrestationBean extends AbstractScreenBean { private List<Integer> integers; private HtmlDataTable dataTable; private String origin; private Integer randomInteger; public SearchPrestationBean() { } /** * {@inheritDoc} */ @Override public String initPage() { randomInteger = null; return "searchPrestation"; } public void addDivergenceBetweenSearchCriteriaAndResultsMessage() { getCommonHelper().addMessage("validation.error.searchAndResultSynchronization"); } public String search() { Random random = new Random(); setIntegers(Arrays.asList(new Integer(random.nextInt()))); return null; } public String getOrigin() { return this.origin; } public void setOrigin(String origin) { this.origin = origin; } public String getDisplayInfo() { randomInteger = getCurrentInteger(); return randomInteger.toString(); } public HtmlDataTable getDataTable() { return this.dataTable; } public void setDataTable(HtmlDataTable dataTable) { this.dataTable = dataTable; } public Integer getCurrentInteger() { if (dataTable != null) { return (dataTable.getRowCount() != 0 ? (Integer) dataTable.getRowData() : null); } return null; } public List<Integer> getIntegers() { return this.integers; } public void setIntegers(List<Integer> integers) { this.integers = integers; }
Tell me if you need more information.
Thank you in advance for your answer.
Nicolas.