I have a JSF page which has a variable number inputText elements containing numeric weights. These are all bound to Weight objects in my backing bean. I’d like to create a single actionListener button which will re-distribute the weights across all the input texts.
I can call the method in the backing bean which distributes the values contained in the weight objects within the backing bean, but for some reason those updated values are not reflected in the InputText elements.
It is my assumption that the values are being put back in the UI elements before I update the values. Just shows my lack of understanding of the JSF lifecycle.
Can someone tell me how I could accomplish this?
Here is the relevant part of the xhtml file. For each child I reference the “newWeight” object key’d by the child object. The newWeight objects are created in the loadFamily method which is bound to the preRenderView event:
<!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:f="http://java.sun.com/jsf/core"
xmlns:ui="http://java.sun.com/jsf/facelets"
xmlns:a4j="http://richfaces.org/a4j"
xmlns:rich="http://richfaces.org/rich"
xmlns:h="http://java.sun.com/jsf/html">
<ui:composition template="/templates/layout.xhtml">
<f:metadata>
<f:viewParam name="familyId" required="true"
requiredMessage="familyId is required"
value="#{weighFamilyBacking.familyId}"></f:viewParam>
<f:event type="preRenderView"
listener="#{weighFamilyBacking.loadFamily}" />
</f:metadata>
<ui:define name="title">Weigh Family</ui:define>
<ui:define name="content">
<h:form>
<h2>Previous Weights</h2>
<rich:dataTable value="#{weighFamilyBacking.allWeights}" var="weight"
id="table">
<rich:column>
<f:facet name="header">
<h:outputText value="Child" />
</f:facet>
<h:outputText
value="#{weight.child.firstName} #{weight.child.lastName}" />
</rich:column>
<rich:column>
<f:facet name="header">
<h:outputText value="Weight" />
</f:facet>
<h:outputText value="#{weight.weight}" />
</rich:column>
<rich:column>
<f:facet name="header">
<h:outputText value="Weigh Time" />
</f:facet>
<h:outputText value="#{weight.weighTime}" />
</rich:column>
<rich:column>
<f:facet name="header">
<h:outputText value="Payout Time" />
</f:facet>
<h:outputText value="#{weight.payoutTime}" />
</rich:column>
<rich:column>
<f:facet name="header">
<h:outputText value="Payout Status" />
</f:facet>
<h:outputText value="#{weight.status}" />
</rich:column>
</rich:dataTable>
<h2>New Weights</h2>
<h:panelGroup id="newWeights">
<ul>
<ui:repeat var="child" value="#{weighFamilyBacking.children}">
<li><h:panelGroup layout="block">
<h:outputText value="#{child.firstName}" />
<h:outputLabel value="Lbs" for="lbs">
<h:inputText size="3" id="lbs"
value="#{weighFamilyBacking.newWeights[child].lbs}" />
</h:outputLabel>
<h:outputLabel value="Oz" for="oz">
<h:inputText id="oz" size="3"
value="#{weighFamilyBacking.newWeights[child].oz}" />
</h:outputLabel>
</h:panelGroup></li>
</ui:repeat>
<h:outputLabel value="Donate Only" for="donate">
<h:selectBooleanCheckbox id="donate"
value="#{weighFamilyBacking.donateOnly}" />
</h:outputLabel>
</ul>
</h:panelGroup>
<h:commandButton
actionListener="#{weighFamilyBacking.distributeWeights}"
immediate="true" value="Redistribute" />
<h:commandButton action="cancel" value="Cancel" />
<h:commandButton action="#{weighFamilyBacking.save}" value="Save" />
</h:form>
</ui:define>
</ui:composition>
</html>
Here is the backing bean:
@ViewScoped
@ManagedBean
public class WeighFamilyBacking extends BaseForm implements Serializable {
private static final long serialVersionUID = 3710213437377609887L;
private Integer familyId;
private Boolean donateOnly;
public Boolean getDonateOnly() {
return donateOnly;
}
public void distributeWeights(ActionEvent event) {
Integer oz = 0;
Integer count = 0;
for (WebWeight ww : newWeights.values()) {
System.out.println(ww);
oz += ww.getLbs() * 16;
oz += ww.getOz();
ww.setLbs(123); // Set the values to something to simulate re-distribution for now.
ww.setOz(456);
count++;
}
donateOnly = true;
}
public void setDonateOnly(Boolean donateOnly) {
this.donateOnly = donateOnly;
}
private Family family;
private HashMap<Child, WebWeight> newWeights;
public HashMap<Child, WebWeight> getNewWeights() {
return newWeights;
}
public void setNewWeights(HashMap<Child, WebWeight> newWeights) {
this.newWeights = newWeights;
}
public WeighFamilyBacking() {
newWeights = new HashMap<Child, WebWeight>();
}
public List<Weight> getAllWeights() {
List<Weight> weights = new ArrayList<Weight>();
for (Child c : getFamily().getChildrenAsList()) {
for (Weight w : c.getWeightsAsList())
weights.add(w);
}
Collections.sort(weights, new Comparator<Weight>() {
@Override
public int compare(Weight arg0, Weight arg1) {
if (arg0.getWeighTime() == null)
return -1;
Integer date = arg0.getWeighTime().compareTo(
arg1.getWeighTime());
if (date == 0)
return arg0.getChild().getFirstName()
.compareTo(arg1.getChild().getFirstName());
return date;
}
});
return weights;
}
@PostConstruct
public void init() {
}
public void loadFamily() {
if (family == null) {
setFamily(hcbbService.findFamilyById(getFamilyId()));
for (Child c : family.getChildrenAsList()) {
WebWeight w = new WebWeight();
newWeights.put(c, w);
}
}
}
public void setFamilyId(Integer id) {
this.familyId = id;
}
public Integer getFamilyId() {
return this.familyId;
}
public Family getFamily() {
return family;
}
public void setFamily(Family f) {
this.family = f;
}
public List<Child> getChildren() {
List<Child> children = getFamily().getChildrenAsList();
Collections.sort(children);
return children;
}
public String cancel() {
return "cancel";
}
public String save() {
for (Child c : newWeights.keySet()) {
WebWeight ww = newWeights.get(c);
Weight w = new Weight();
w.setWeighTime(new Date());
Double weight = (ww.getLbs() * 16.0 + ww.getOz()) / 16.0;
w.setWeight(weight);
w.setStatus(WeightStatus.AWAITING_PAYOUT);
c.getWeights().add(w);
}
hcbbService.updateRegistration(family);
return "success";
}
}
The goal is to allow us to put weights in a single child and then have it evenly distribute the values across all “newWeight” objects. Right now I would expect that all my UI elements linked to the NewWeight objects would be zero after I click redistribute (since that method is getting called, and the values are being reset), but they aren’t.
Additional Info
My adjustment of the WebWeight objects is happening at the INVOKE_APPLICATION phase as expected. The objects have the correct values before Render response.. but not in the rendered components?
From Log:
BEFORE INVOKE_APPLICATION 5
net.halo3.hcbb.registration.WebWeight@2f6cd09f
net.halo3.hcbb.registration.WebWeight@10f48f0c
net.halo3.hcbb.registration.WebWeight@27db6586
AFTER INVOKE_APPLICATION 5
BEFORE RENDER_RESPONSE 6
oz=456 lbs=123 object=net.halo3.hcbb.registration.WebWeight@2f6cd09f
oz=456 lbs=123 object=net.halo3.hcbb.registration.WebWeight@10f48f0c
oz=456 lbs=123 object=net.halo3.hcbb.registration.WebWeight@27db6586
AFTER RENDER_RESPONSE 6
More Info
Here is the dump from logs, where I log getLbs and getOz.. You can see that getLbs or getOz on the WebWeight object is not called during the Render phase?
- BEFORE INVOKE_APPLICATION 5
net.halo3.hcbb.registration.WebWeight@12d58dfe
net.halo3.hcbb.registration.WebWeight@12d58dfe getLbz: 0
net.halo3.hcbb.registration.WebWeight@12d58dfe getOz:0
net.halo3.hcbb.registration.WebWeight@25d285b
net.halo3.hcbb.registration.WebWeight@25d285b getLbz: 0
net.halo3.hcbb.registration.WebWeight@25d285b getOz:0
net.halo3.hcbb.registration.WebWeight@6c317dc9
net.halo3.hcbb.registration.WebWeight@6c317dc9 getLbz: 0
net.halo3.hcbb.registration.WebWeight@6c317dc9 getOz:0
- AFTER INVOKE_APPLICATION 5
- BEFORE RENDER_RESPONSE 6
- AFTER RENDER_RESPONSE 6
It turned out that the problem was some of the xhtml. When I cleaned it all out and started simple it started working. I’m thinking maybe the outputLabels or the panelGroups.