JSF - 如何在立即采取行动后将瞬态值保留在 ui:repeat 中

JSF - How can I retain the transient values within a ui:repeat after an immediate action

我根据我的项目创建了一个非常简单的示例来说明我的疑问。只是一种用电话 phone 号码列表注册一个人的方法。

MainController.java

private String name;
private List<Phone> phoneList;
// Getters and Setters

@PostConstruct
private void init() {
    phoneList = new ArrayList<>();
}

public static class Phone implements Serializable {

    private String number;
    // Getters and Setters

    @Override
    public String toString() {
        return number != null ? number : "null";
    }
}

public void add() {
    phoneList.add(new Phone());
}

public void save() {
    System.out.println("Name: " + name + "; " + phoneList.toString());
}

index.xhtml

<h:form>
    <h:inputText value="#{mainController.name}" required="true" />
    <ui:repeat var="phone" value="#{mainController.phoneList}" varStatus="status">
        <h:inputText value="#{phone.number}" required="true" />
    </ui:repeat>
    <h:commandButton action="#{mainController.add()}" value="Add Phone" immediate="true" />
    <h:commandButton action="#{mainController.save()}" value="Save" />
</h:form>

在我的示例中,请注意必须填写所有添加的 phone 字段(必需 = true)。

问题是:当我键入 name 并单击 add(添加一个 phone)时字段被维护。但是,当我先键入 phone 并单击 add 时,phone 的值不会保留。对于组件 ui:repeat.

中的所有字段都会发生这种情况

有没有办法像 name 字段那样在立即请求后保留输入值?


额外说明: 我注意到的其他奇怪行为是添加至少两个 phone 字段时,让第一个空白并填充第二个,然后保存表格。验证失败后(由于 phone 空白),单击添加将使所有字段填充第二个 phone.

的值

Wildfly 9.0.2,JSF Api (Jboss) 2.2.12

我对这个问题使用的解决方案是为循环创建一个外部字段,它存储一个 JSON,其中包含应保存的值。该字段在循环之外,在每次尝试后正确保存值,并在必要时恢复缺失值。我使用两个函数 JavaScript 和 JQuery 库。

所以文件看起来像这样:

index.xhtml

<h:outputScript library="jquery" name="jquery.min.js" />
<h:outputScript library="all" name="all.js" />

<h:form>
    <h:inputText value="#{mainController.name}" required="true" />
    <ui:repeat var="phone" value="#{mainController.phoneList}" varStatus="status">
        <h:inputText styleClass="savePhoneNumber" value="#{phone.number}" required="true" onchange="saveUiRepeatInput('#{allPhoneNumber.clientId}', 'savePhoneNumber')" />
    </ui:repeat>

    <h:inputHidden id="allPhoneNumber" binding="#{allPhoneNumber}" />
    <h:outputScript>loadUiRepeatInput('#{allPhoneNumber.clientId}', 'savePhoneNumber')</h:outputScript>

    <h:commandButton action="#{mainController.add()}" value="Add Phone" immediate="true" />
    <h:commandButton action="#{mainController.save()}" value="Save" />
</h:form>

all.js

function saveUiRepeatInput(inputAll, inputClass) {
    document.getElementById(inputAll).value = JSON.stringify($('.' + inputClass).map(function() { return this.value; }).get());
}

function loadUiRepeatInput(inputAll, inputClass) {
    var jsonAll = document.getElementById(inputAll).value;
    if (jsonAll) {
        var array = JSON.parse(jsonAll);
        $('.' + inputClass).each(function(i) { if (i < array.length) this.value = array[i]; });
    }
}

虽然工作完美(包括通过 ajax,有一些小的改动),但它看起来像是一个 hack,而不是一个理想的解决方案。因此,如果有人可以提供严格基于 JSF 的任何解决方案,我将不胜感激。谢谢

感谢@BalusC . The OmniFaces 库有两个可以在这种情况下使用的标记处理程序。在这两种情况下,输入值都将被保留以防验证失败。注意 h:commandButton 应该与 <h:commandButton immediate="false" />.

ignoreValidationFailed

在这种情况下,所有验证失败都将被忽略(包括转换器失败)。请注意,h:form 必须更改为 o:form。此外,失败消息仍将显示,这可以通过在渲染属性中设置适当的条件来解决。这些文件将如下所示:

index.xhtml

<o:form>
    <h:inputText value="#{mainController.name}" required="true" />
    <ui:repeat var="phone" value="#{mainController.phoneList}" varStatus="status">
        <h:inputText value="#{phone.number}" required="true" />
    </ui:repeat>
    <h:commandButton action="#{mainController.add()}" value="Add Phone">
        <o:ignoreValidationFailed />
    </h:commandButton>
    <h:commandButton action="#{mainController.save()}" value="Save" />
</o:form>
<h:messages rendered="#{facesContext.validationFailed}" />

skipValidators

在这种情况下,只会忽略验证失败(转换器仍会 运行)。除转换器外,不会显示故障消息。请注意,此标记处理程序仅在 2.3 版本后可用。这些文件将如下所示:

index.xhtml

<h:form>
    <h:inputText value="#{mainController.name}" required="true" />
    <ui:repeat var="phone" value="#{mainController.phoneList}" varStatus="status">
        <h:inputText value="#{phone.number}" required="true" />
    </ui:repeat>
    <h:commandButton action="#{mainController.add()}" value="Add Phone">
        <o:skipValidators />
    </h:commandButton>
    <h:commandButton action="#{mainController.save()}" value="Save" />
</h:form>