I have a problem with the correct usage of the methods getValue()
, getLocalValue()
and/or getSubmittedValue()
I tried to write a simple Composite Component to let the user enter a time (hours and minutes). In the model I want to store only one Integer value (hours*60+minutes).
Basically I followed this article from BalusC.
Now I want to submit the form where I am using this component and persist the value to my database, but I am getting the previous value instead.
There are no validation errors, no converter errors etc. Thus, as far as I understood, in the invoke-Phase, when the action-Method is triggered, the getSubmittedValue()
should return null
, the new value should be accessable via getValue()
and isLocalValueSet()
should return true
. (see also here, where a similar question is asked)
Am I correct so far?
But what happens is that isLocalValueSet()
returns true
but getValue()
(and also getLocalValue()
) gives me the old value and getSubmittedValue()
is returning the new (correct) value.
On the second submit, getValue()
returns the value, just entered before the first button click.
So, what am I missing / doing wrong here?
My Code so far: Backing Component:
@FacesComponent("inputTime")
public class InputTime extends UIInput implements NamingContainer {
private UIInput hours;
private UIInput minutes;
@Override
public String getFamily() {
return UINamingContainer.COMPONENT_FAMILY;
}
@Override
public void encodeBegin(FacesContext context) throws IOException {
Integer i = (Integer) this.getValue();
if(i != null) {
hours.setValue(i/60);
minutes.setValue(i%60);
}
super.encodeBegin(context);
}
@Override
public Object getSubmittedValue() {
Integer [] value = new Integer [2];
value[0] = hours.isLocalValueSet() ?
(Integer) hours.getValue() : (Integer) hours.getSubmittedValue();
value[1] = minutes.isLocalValueSet() ?
(Integer) minutes.getValue() : (Integer) minutes.getSubmittedValue();
return value;
}
@Override
protected Object getConvertedValue(FacesContext contect, Object submittedValue) {
Integer [] value = (Integer []) submittedValue;
int hh = value[0] == null ? 0 : value[0];
int mm = value[1] == null ? 0 : value[1];
return hh*60+mm;
}
// Getter & Setter
}
The Composite Component:
<cc:interface componentType="inputTime">
<cc:attribute name="value" type="java.lang.Integer" required="true" />
<cc:attribute name="ajax" default="#{false}" type="java.lang.Boolean" />
<cc:attribute name="listener" method-signature="void actionListener()" />
</cc:interface>
<cc:implementation>
<span id="#{cc.clientId}" style="white-space: nowrap">
<p:inputText id="hours" binding="#{cc.hours}" converter="javax.faces.Integer" >
</p:inputText>
<p:inputText id="minutes" binding="#{cc.minutes}" converter="javax.faces.Integer">
</p:inputText>
</span>
</cc:implementation>
Usage in View:
<h:form>
<stg:inputTime value="#{bean.entity.value}"/>
<p:commandButton action="#{bean.listener}" />
</h:form>
I am running this specific project on:
- Mojarra 2.1.29
- PrimeFaces 5.1
- GlassFish 3.1.2 Build 5
Edit 1:
According to BalusC comment, I edited the Backing Component like this:
@Override
public Object getSubmittedValue() {
StringBuilder sb = new StringBuilder("");
sb.append(hours.isLocalValueSet() ?
hours.getValue() : hours.getSubmittedValue());
sb.append("-");
sb.append(minutes.isLocalValueSet() ?
minutes.getValue() : minutes.getSubmittedValue());
return sb.toString();
}
@Override
protected Object getConvertedValue(FacesContext contect, Object submittedValue) {
Scanner sc = new Scanner((String) submittedValue).useDelimiter("-");
int hh = sc.nextInt();
int mm = sc.nextInt();
return hh*60+mm;
}
But my problem remains the same:
- in my getSubmittedValue
method isLocalValueSet
is evaluated to true
and getValue
(as well as getLocalValue
) is returning the 'old' value.
(For clarification, I am not dealing with null values here so far, but I just wanted to keep it simple in this example..)
Edit 2: I just upgraded from PrimeFaces 3.5 to PrimeFaces 5.1. Problem is still present.