Mojarra 2.1.7-jbossorg-1
I'm out of my wits trying to figure out what is going on here.
I have broken down the problem that I am seeing to something smaller test case that is easily reproducable.
Two <h:selectOneRadio> ( Approved and Notified )
One <h:selectOneMenu> wrapped in a <h:panelGroup>
One <h:commandButton>
The list of contents of the <h:selectOneMenu> is dependent on the value of the "Approved" <h:selectOneRadio>, and the list is obtained from the viewBean.
Apart from the "required" attribute, the only other validation is a validator method in the bean that:
If "Approved" radio button is Yes, and "Notified" radio button is No, then a ValidatorException is thrown and is shown on the <h:messages>
Now the issue:
1) Select Approved "Yes"
2) Select Notified "No"
3) Select dropdown to PEND
4) Select Submit button
5) <h:messages> show "Notified must be true when action is PEND". All good.
6) Change Approved to "No" ... Note that there is a listener on change of Approved ... and the listener will always set the value of the dropdown to null.
Because I changed the value of the Approved radio button, the dropdown selection is then changed to the "- Select- " item, which is what I am expecting as that is what the listener method does ... and the dropdown is re-rendered.
7) Change Approved back to "Yes" ... dropdown changes back to PEND ... This is what I do not understand.
Why was it being changed back to PEND when:
A) The listener on the Approved radio button always sets the value of the dropdown to null in the backend.. and
B) The Approved render attribute always re-render the dropdown
???
<!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:h="http://java.sun.com/jsf/html"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:ui="http://java.sun.com/jsf/facelets">
<h:head></h:head>
<body>
<h:form>
<h:outputLabel value="Approved:"/>
<h:selectOneRadio id="approved" value="#{viewBean.approved}"
required="true"
requiredMessage="Approved is required.">
<f:selectItem itemValue="true" itemLabel="Yes"/>
<f:selectItem itemValue="false" itemLabel="No"/>
<f:ajax
event="valueChange"
execute="@this"
render="nextActionPanel"
listener="#{viewBean.triggerApprovedChange()}"/>
</h:selectOneRadio>
<h:outputLabel value="Notified:"/>
<h:selectOneRadio id="notified" value="#{viewBean.notified}"
required="true"
requiredMessage="Notified is required."
validator="#{viewBean.validateNotified}">
<f:selectItem itemValue="true" itemLabel="Yes"/>
<f:selectItem itemValue="false" itemLabel="No"/>
<f:ajax
event="valueChange"
execute="@this"
render="@none"/>
</h:selectOneRadio>
<h:panelGroup id="nextActionPanel">
<h:selectOneMenu id="nextAction"
required="true"
requiredMessage="Next Action is required."
value="#{viewBean.nextAction}">
<f:selectItem itemValue="" itemLabel="- Select -" />
<f:selectItems
value="#{viewBean.availableNextActions}" var="target"
itemValue="#{target.value}"
itemLabel="#{target.label}"/>
<f:ajax
event="valueChange"
execute="@this"
render="@none"/>
</h:selectOneMenu>
</h:panelGroup>
<h:commandButton id="submit" value="Submit"/><br/>
<h:messages/>
</h:form>
</body>
</html>
package test;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import javax.annotation.PostConstruct;
import javax.faces.application.FacesMessage;
import javax.faces.bean.ManagedBean;
import javax.faces.bean.ViewScoped;
import javax.faces.component.UIComponent;
import javax.faces.context.FacesContext;
import javax.faces.validator.ValidatorException;
@ManagedBean
@ViewScoped
public class ViewBean implements Serializable {
private static final long serialVersionUID = 1L;
private Boolean approved;
private Boolean notified;
private String nextAction;
private List<NextAction> availableNextActions;
public Boolean getApproved() {
return approved;
}
public void setApproved(Boolean selection) {
this.approved = selection;
}
public Boolean getNotified() {
return notified;
}
public void setNotified(Boolean selection2) {
this.notified = selection2;
}
public String getNextAction() {
return nextAction;
}
public void setNextAction(String nextAction) {
this.nextAction = nextAction;
}
public void triggerApprovedChange() {
changeAvailableNextActions();
setNextAction(null);
}
public List<NextAction> getAvailableNextActions() {
return availableNextActions;
}
private void setAvailableNextActions(List<NextAction> availableNextActions) {
this.availableNextActions = availableNextActions;
}
public void changeAvailableNextActions() {
List<NextAction> nextActions = new ArrayList<NextAction>();
if( Boolean.TRUE.equals( getApproved() )) {
nextActions.add( new NextAction("PEND", "Pend"));
}
nextActions.add( new NextAction("REQADVICE", "Request Advice"));
nextActions.add( new NextAction("FIN", "Finish"));
setAvailableNextActions(nextActions);
}
@PostConstruct
public void init() {
changeAvailableNextActions();
}
public void validateNotified(
FacesContext context,
UIComponent toValidate,
Object value)
throws Exception
{
List<FacesMessage> messages = new ArrayList<FacesMessage>();
Boolean isNotified = (Boolean) value;
if( Boolean.FALSE.equals( isNotified ) && "PEND".equals( nextAction )) {
FacesMessage message = new FacesMessage(FacesMessage.SEVERITY_ERROR,
"Notified must be true when action is PEND", null);
messages.add( message );
}
if( messages.size() > 0 ) {
throw new ValidatorException(messages);
}
}
public class NextAction implements Serializable {
private static final long serialVersionUID = 1L;
private String label;
private String value;
public NextAction(String aValue, String aLabel) {
value = aValue;
label = aLabel;
}
public String getLabel() {
return label;
}
public String getValue() {
return value;
}
}
}