从支持组件接收 "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>

我在 运行 这个具体项目上:


编辑 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();
}