I have a problem on my JSF page where I type a name into the first billing first name field. If I click on copy billing address checkbox; this stops rendering the delivery address panel and hides it via ajax, the value I just typed into the first field gets reset to its previous state.
JSF Page
<h:form>
<p:inputText value="#{addressBean.billingAddress.firstName}" required="true"/ >
<p:selectBooleanCheckbox value="#{addressBean.copyBillingAddress}" id="duplicateBillingDetails">
<f:ajax render="@form" />
</p:selectBooleanCheckbox>
<h:panelGrid rendered="#{not addressBean.copyBillingAddress}" columns="3">
<p:inputText value="#{addressBean.deliveryAddress.firstName}"/>
</h:panelGrid>
<p:commandButton value="Checkout" action="#{addressBean.saveAddress}"/>
</h:form>
Backing Bean
@Component
@Scope("view")
public class AddressBean implements Serializable {
@Inject
private CurrentUserBean currentUserBean;
@Inject
private UserService userService;
private Address deliveryAddress = new Address();
private Address billingAddress = new Address();
private boolean copyBillingAddress;
public AddressBean() {
}
public boolean isCopyBillingAddress() {
return copyBillingAddress;
}
public void setCopyBillingAddress(boolean copyBillingAddress) {
this.copyBillingAddress = copyBillingAddress;
}
public String saveAddress() {
if (copyBillingAddress) {
deliveryAddress = new Address(billingAddress);
}
User user = currentUserBean.getUser();
if (!billingAddress.isSame(user.getBillingAddress())) {
user.setBillingAddress(billingAddress);
}
if (!deliveryAddress.isSame(user.getDeliveryAddress())) {
user.setDeliveryAddress(deliveryAddress);
}
currentUserBean.setUser(userService.save(user));
return "/checkout.xhtml";
}
public CurrentUserBean getCurrentUserBean() {
return currentUserBean;
}
public void setCurrentUserBean(CurrentUserBean currentUserBean) {
this.currentUserBean = currentUserBean;
}
public Address getDeliveryAddress() {
return deliveryAddress;
}
public void setDeliveryAddress(Address deliveryAddress) {
this.deliveryAddress = deliveryAddress;
}
public Address getBillingAddress() {
return billingAddress;
}
public void setBillingAddress(Address billingAddress) {
this.billingAddress = billingAddress;
}
}
The
<f:ajax>processes by default only the current component as inexecute="@this". So the submitted values of all other input components won’t be processed and thus not be updated into the model. However, you’re byrender="@form"forcing the entire HTML output of the form to be refreshed with current (non-updated!) model values of all other input components.Assuming that your intent is to not unnecessarily convert/validate/update all other input components, you’d better make the
renderattribute more specific. Update only the content which really needs to be updated and not the entire form.Or, if you really need the
render="@form", then you need to add aexecute="@form"in order to tell<f:ajax>to process all other input components as well.Note that this may unnecessarily trigger conversion/validation on those input components.
See also: