JSF : Changing component style in jstl c:foreach array not working
This code displays an index and performs an action using RichFaces' <a4j:commandLink>
tag. It works technically fine. Only the style of previously selected letter is not been reset (altough the appropriate code part is executed). Does anybody know where the problem is and how to solve it?
The JSF page:
<?xml version="1.0" encoding="ISO-8859-1"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:ui="http://java.sun.com/jsf/facelets" xmlns:h="http://java.sun.com/jsf/html" xmlns:f="http://java.sun.com/jsf/core" xmlns:c="http://java.sun.com/jstl/core" xmlns:rich="http://richfaces.org/rich" xmlns:a4j="http://richfaces.org/a4j">
<head>
<style>
a.selected {
text-decoration: underline;
}
a.unselected {
text-decoration: none;
}
</style>
</head>
<body>
<rich:panel id="result">
hello
<h:form id="myForm">
<c:forEach var="letter" items="#{testBean.letters}" >
<a4j:commandLink id="${letter}" value="${letter}" actionListener="#{testBean.startWith}" reRender="result" styleClass="unselected"/> 
</c:forEach>
</h:form>
</rich:panel>
<a4j:keepAlive beanName="testBean" />
</body>
</html>
Note that it is using <a4j:keepalive>
to keep the backing bean alive between Ajax requests.
The backing bean (request scoped):
public class testBean
{
private String startLetter = null; // selects from alphabetical page
private String[] letters = { "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z" };
private UIComponent currentStartWithLetter;
public void startWith(ActionEvent actionEvent)
{
FacesContext facesContext = FacesContext.getCurrentInstance();
ELContext elContext = facesContext.getELContext();
Application app = facesContext.getApplication();
ExpressionFactory elFactory = app.getExpressionFactory();
ValueExpression valueExp;
if (currentStartWithLetter != null) {
valueExp = elFactory.createValueExpression(elContext, "unselected",Object.class);
currentStartWithLetter.setValueExpression("styleClass", valueExp);
}
currentStartWithLetter = actionEvent.getComponent();
startLetter = currentStartWithLetter.getId();
valueExp = elFactory.createValueExpression(elContext, "selected",Object.class);
currentStartWithLetter.setValueExpression("styleClass", valueExp);
someAction(actionEvent);
}
public void setLetters(String[] letters) {
/* nothing to do */
}
public String[] getLetters() {
return letters;
}
public String getStartLetter() {
return startLetter;
}
public void setStartLetter(开发者_运维知识库String startLetter) {
this.startLetter = startLetter;
}
}
I always feel that if I'm referencing UIComponent
in my code I'm doing something the hard way. Here's how I'd do this.
Try using <f:setPropertyActionListener>
and <a4j:repeat>
in place of actionListener
and <c:forEach>
. Also, avoid rerendering whole forms, it causes issues in some browsers:
<h:panelGroup id="results">
<a4j:repeat var="letterBean" items="#{testBean.letters}" >
<a4j:commandLink id="letter" value="#{letterBean.letter}" reRender="results" styleClass="#{letterBean.selected ? 'selected' : 'unselected'}">
<f:setPropertyActionListener value="#{letterBean}" target="#{testBean.selected}"/>
</a4j:commandLink> 
</a4j:repeat>
</h:panelGroup>
New bean class:
public class LetterBean {
private final String letter;
private boolean selected;
public LetterBean (String letter) {
this.letter = letter;
}
//getters and setters
}
replace startWith
with:
public void setSelected(LetterBean selectedBean) {
for (LetterBean letter : letterBeans) {
letter.setSelected(false);
}
selectedBean.setSelected(true);
}
I modified the code changing :
if (currentStartWithLetter != null) {
valueExp = elFactory.createValueExpression(elContext, "unselected",Object.class);
currentStartWithLetter.setValueExpression("styleClass", valueExp);
}
into
if (currentStartWithLetter != null) {
valueExp = elFactory.createValueExpression(elContext, "unselected",Object.class);
currentStartWithLetter = (UIComponent) actionEvent.getComponent().findComponent(startLetter);
currentStartWithLetter.setValueExpression("styleClass", valueExp);
}
and it works.
The problem seems to be that currentStartWithLetter
does not refer to the same component anymore because the actionEvent is different between calls.
精彩评论