问题
I have the following dropdown which list couple of cars, I have it so that it stores the value of selected item in a backbean variable and an event is fired so other dropdowns would fill up according to the selected value of this dropdown control as below:
<Td>
<h:selectOneMenu id="combocarList"
value="#{customerBean.selectedcar}"
styleClass="comboStyle"
valueChangeListener="#{customerBean.loadothercombos}"
onchange="document.forms[0].submit()"
>
<f:selectItem
itemLabel="-----------Select--------------"
itemValue="None" />
<f:selectItems value="#{customerBean.carsList}" />
</h:selectOneMenu>
</Td>
Problem is when an item selected from the above dropdown, the event loadothercombos is called before the setter which causes problems.
Note that the backbean customer is defined as:
<managed-bean-name>customerBean</managed-bean-name>
<managed-bean-class>com.theway.customer</managed-bean-class>
<managed-bean-scope>session</managed-bean-scope>
The behaviour i see in debugging that when i select an item from dropdown:
1) Getter is called for selectedcar
2) Loadothercombos is called <------- This is called by the event
3) Setter is called for selectedcar
I cannot get it to call the setter before calling loadothercombos. Any insight would be appreciated. Thanks
回答1:
Problem is when an item selected from the above dropdown, the event loadothercombos is called before the setter
Well, is the expected JSF life cycle behavior. Your valueChangeListener="#{customerBean.loadothercombos}"
is invoked during validation phase after a succesful conversion/validation of the submitted value and only when the submitted value differs from the initial value. After invoking the valueChangeListener, JSF will continue with conversion/validation of the next UIInput and when JSF implementation determines that the data is valid, then proceed to the next Update Model Values Phase
invoking your setter method value="#{customerBean.selectedcar}"
回答2:
Using valueChangeListener
for this purpose has always been a hacky way. Basically, there are two ways to work around this "problem":
Call FacesContext#renderResponse() so that JSF will immediately move to the render response phase and thus the update model values (and invoke action) phase will be skipped:
public void loadothercombos(ValueChangeEvent event) { selectedcar = (String) event.getNewValue(); loadOtherCombosBasedOn(selectedcar); // ... FacesContext.getCurrentInstance().renderResponse(); }
Queue the event to invoke action phase so that it does its job after the setter being called:
public void loadothercombos(ValueChangeEvent event) { if (event.getPhaseId() == PhaseId.INVOKE_APPLICATION) { loadOtherCombosBasedOn(selectedcar); } else { event.setPhaseId(PhaseId.INVOKE_APPLICATION); event.queue(); } }
If you're using JSF 2.0, then there's a much easier way to handle this with help of <f:ajax>
:
<h:selectOneMenu id="combocarList"
value="#{customerBean.selectedcar}"
styleClass="comboStyle">
<f:selectItem
itemLabel="-----------Select--------------"
itemValue="None" />
<f:selectItems value="#{customerBean.carsList}" />
<f:ajax listener="#{customerBean.loadOtherCombos}" render="otherComboIds" />
</h:selectOneMenu>
with
public void loadothercombos() {
loadOtherCombosBasedOn(selectedcar);
}
Unrelated to the concrete problem: a "combobox" is the incorrect term for this dropdown element. A combobox is an editable dropdown, it's basically a combination of <input type="text">
and <select>
. What you've there just renders alone <select>
and those are just dropdowns, so call them as such as well.
来源:https://stackoverflow.com/questions/7722351/event-function-called-before-setter