从支持组件接收 "old" 值 - getValue、getLocalValue、getSubmittedValue 的正确用法
Receiving "old" value from Backing Component - correct usage of getValue, getLocalValue, getSubmittedValue
我对方法 getValue()
、getLocalValue()
and/or getSubmittedValue()
的正确使用有疑问
我尝试编写一个简单的复合组件让用户输入时间(小时和分钟)。在模型中我只想存储一个整数值(小时*60+分钟)。
基本上我关注了 BalusC 的 this 文章。
现在我想提交我正在使用该组件的表单并将值保存到我的数据库中,但我得到的是以前的值。
没有验证错误,没有转换器错误等。因此,据我所知,在调用阶段,当触发操作方法时,getSubmittedValue()
应该 return null
,新值应该可以通过 getValue()
访问,isLocalValueSet()
应该 return true
。 (另请参阅 here,其中提出了类似的问题)
到目前为止,我是否正确?
但是 isLocalValueSet()
returns true
但是 getValue()
(还有 getLocalValue()
)给了我旧值 getSubmittedValue()
是 returning 新的(正确的)值。
在第二次提交时,getValue()
returns 值,刚好在第一次按钮单击之前输入。
那么,我在这里遗漏了什么/做错了什么?
到目前为止我的代码:
支持组件:
@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
}
复合组件:
<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>
视图中的用法:
<h:form>
<stg:inputTime value="#{bean.entity.value}"/>
<p:commandButton action="#{bean.listener}" />
</h:form>
我在 运行 这个具体项目上:
- Mojarra 2.1.29
- PrimeFaces 5.1
- GlassFish 3.1.2 版本 5
编辑 1:
根据 BalusC 的评论,我这样编辑了支持组件:
@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;
}
但我的问题还是一样:
- 在我的 getSubmittedValue
方法中 isLocalValueSet
被评估为 true
并且 getValue
(以及 getLocalValue
)是 returning 'old' 价值。
(为了澄清起见,到目前为止我没有在这里处理空值,但我只是想在这个例子中保持简单..)
编辑 2:
我刚刚从 PrimeFaces 3.5 升级到 PrimeFaces 5.1。问题仍然存在。
That article 在用户报告它在 MyFaces 中不起作用后被编辑。原来的 getSubmittedValue()
是这样写的:
@Override
public Object getSubmittedValue() {
return day.getSubmittedValue()
+ "-" + month.getSubmittedValue()
+ "-" + year.getSubmittedValue();
}
然后一位 MyFaces 用户报告说这在 MyFaces 中失败了,因为它首先处理输入的子项,然后处理输入本身,而 Mojarra 恰恰相反。这种不一致将针对 JSF 2.3 进行调整(可能会遵循 MyFaces 方法)。
错误修复如下:
@Override
public Object getSubmittedValue() {
return (day.isLocalValueSet() ? day.getValue() : day.getSubmittedValue())
+ "-" + (month.isLocalValueSet() ? month.getValue() : month.getSubmittedValue())
+ "-" + (year.isLocalValueSet() ? year.getValue() : year.getSubmittedValue());
}
但是,它忽略了 isLocalValueSet
存储在 JSF 状态中的事实,因此它在 Mojarra 中仍然会在多次后续提交中失败,并且只有 return 最初提交的值。
下面应该再次修复它,并使它更难看:
@Override
public Object getSubmittedValue() {
return (day.getSubmittedValue() == null && day.isLocalValueSet() ? day.getValue() : day.getSubmittedValue())
+ "-" + (month.getSubmittedValue() == null && month.isLocalValueSet() ? month.getValue() : month.getSubmittedValue())
+ "-" + (year.getSubmittedValue() == null && year.isLocalValueSet() ? year.getValue() : year.getSubmittedValue());
}
(博客文章已修复)
无论如何,如果您不打算分发此组合,并且总是 运行 在 Mojarra 上,那么您也可以采用初始方法。
@Override
public Object getSubmittedValue() {
return day.getSubmittedValue()
+ "-" + month.getSubmittedValue()
+ "-" + year.getSubmittedValue();
}
我对方法 getValue()
、getLocalValue()
and/or getSubmittedValue()
我尝试编写一个简单的复合组件让用户输入时间(小时和分钟)。在模型中我只想存储一个整数值(小时*60+分钟)。
基本上我关注了 BalusC 的 this 文章。
现在我想提交我正在使用该组件的表单并将值保存到我的数据库中,但我得到的是以前的值。
没有验证错误,没有转换器错误等。因此,据我所知,在调用阶段,当触发操作方法时,getSubmittedValue()
应该 return null
,新值应该可以通过 getValue()
访问,isLocalValueSet()
应该 return true
。 (另请参阅 here,其中提出了类似的问题)
到目前为止,我是否正确?
但是 isLocalValueSet()
returns true
但是 getValue()
(还有 getLocalValue()
)给了我旧值 getSubmittedValue()
是 returning 新的(正确的)值。
在第二次提交时,getValue()
returns 值,刚好在第一次按钮单击之前输入。
那么,我在这里遗漏了什么/做错了什么?
到目前为止我的代码: 支持组件:
@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
}
复合组件:
<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>
视图中的用法:
<h:form>
<stg:inputTime value="#{bean.entity.value}"/>
<p:commandButton action="#{bean.listener}" />
</h:form>
我在 运行 这个具体项目上:
- Mojarra 2.1.29
- PrimeFaces 5.1
- GlassFish 3.1.2 版本 5
编辑 1:
根据 BalusC 的评论,我这样编辑了支持组件:
@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;
}
但我的问题还是一样:
- 在我的 getSubmittedValue
方法中 isLocalValueSet
被评估为 true
并且 getValue
(以及 getLocalValue
)是 returning 'old' 价值。
(为了澄清起见,到目前为止我没有在这里处理空值,但我只是想在这个例子中保持简单..)
编辑 2: 我刚刚从 PrimeFaces 3.5 升级到 PrimeFaces 5.1。问题仍然存在。
That article 在用户报告它在 MyFaces 中不起作用后被编辑。原来的 getSubmittedValue()
是这样写的:
@Override
public Object getSubmittedValue() {
return day.getSubmittedValue()
+ "-" + month.getSubmittedValue()
+ "-" + year.getSubmittedValue();
}
然后一位 MyFaces 用户报告说这在 MyFaces 中失败了,因为它首先处理输入的子项,然后处理输入本身,而 Mojarra 恰恰相反。这种不一致将针对 JSF 2.3 进行调整(可能会遵循 MyFaces 方法)。
错误修复如下:
@Override
public Object getSubmittedValue() {
return (day.isLocalValueSet() ? day.getValue() : day.getSubmittedValue())
+ "-" + (month.isLocalValueSet() ? month.getValue() : month.getSubmittedValue())
+ "-" + (year.isLocalValueSet() ? year.getValue() : year.getSubmittedValue());
}
但是,它忽略了 isLocalValueSet
存储在 JSF 状态中的事实,因此它在 Mojarra 中仍然会在多次后续提交中失败,并且只有 return 最初提交的值。
下面应该再次修复它,并使它更难看:
@Override
public Object getSubmittedValue() {
return (day.getSubmittedValue() == null && day.isLocalValueSet() ? day.getValue() : day.getSubmittedValue())
+ "-" + (month.getSubmittedValue() == null && month.isLocalValueSet() ? month.getValue() : month.getSubmittedValue())
+ "-" + (year.getSubmittedValue() == null && year.isLocalValueSet() ? year.getValue() : year.getSubmittedValue());
}
(博客文章已修复)
无论如何,如果您不打算分发此组合,并且总是 运行 在 Mojarra 上,那么您也可以采用初始方法。
@Override
public Object getSubmittedValue() {
return day.getSubmittedValue()
+ "-" + month.getSubmittedValue()
+ "-" + year.getSubmittedValue();
}