This post originated from an RSS feed registered with Agile Buzz
by Dominik Wei-Fieg.
Original Post: JSF: DataTable and CommandLink
Feed Title: Ars Subtilior
Feed URL: http://typo.ars-subtilior.com/articles.rss
Feed Description: Finest Handmade Code
Random thoughts about agile software development as an art
Like a lot of other programmers who are forced to work with JSF, I fell into THE TRAP!
If you are using ManagedBeans in request scope, you get problems with CommandLinks inside DataTables.
DataTables are one thing I really like about JSF, and CommandLinks often come in handy as well. But when you put a CommandLink inside a DataTable, e. g., to select the entry of the row in which the CommandLink is, you get bitten. That is, if you want ManagedBeans with request scope. The action which should be triggered by the CommandLink is never triggered, the page is simply rendered again.
The reason for this behaviour is that the DataTable modifies the id of the CommandLink during renderering, but the CommandLink does not know that it was rendererd with a different id. During the decoding of the request which was triggered by clicking the CommandLink, the ComandLinkRenderer looks at a hidden form parameter. If the value of that form parameter equals the id of the CommandLink, an action is queued. If not, nothing is done.
Since the DataTable changes the ids, the value of the hidden form parameter does not match the id of the CommandLink.
So I wrote my own CommandLinkRenderer, overwrote the decode method and patched it so that the id match up again. When I want a CommandLink in a DataTable, I then specify that it should use my CommandLinkRenderer.
Here is the source of the decode method:
@Override
public void decode(FacesContext context, UIComponent component)
{
if (context == null || component == null)
{
throw new NullPointerException(Util
.getExceptionMessageString(Util.NULLPARAMETERSERRORMESSAGEID));
}
if (log.isTraceEnabled())
{
log.trace("Begin decoding component " + component.getId());
}
UICommand command = (UICommand) component;
// If the component is disabled, do not change the value of the
// component, since its state cannot be changed.
if (Util.componentIsDisabledOnReadonly(component))
{
if (log.isTraceEnabled())
{
log.trace("No decoding necessary since the component "
+ component.getId() + " is disabled");
}
return;
}
String clientId = command.getClientId(context), paramName = getHiddenFieldName(
context, command);
Map requestParameterMap = context.getExternalContext()
.getRequestParameterMap();
String value = (String) requestParameterMap.get(paramName);
clientId = clientId.substring(0, clientId.lastIndexOf(":"));
value = value.substring(0, value.lastIndexOf(":"));
if (value == null || value.equals("") || !clientId.equals(value))
{
return;
}
ActionEvent actionEvent = new ActionEvent(component);
component.queueEvent(actionEvent);
if (log.isDebugEnabled())
{
log.debug("This command resulted in form submission "
+ " ActionEvent queued " + actionEvent);
}
if (log.isTraceEnabled())
{
log.trace("End decoding component " + component.getId());
}
return;
}