JSF Partial Screen Update обрабатывает полное дерево компонентов

Мне нужен совет JSF/PrimeFaces (3.5).

У нас есть динамическая форма, форма может быть сконфигурирована конструктором формы, а поддержка bean - это прославленная HashMap с некоторыми дополнительными геттерами и сеттерами, такими как (getValueAsDate/setValueAsDate).

Один из наших типов полей позволяет вводить число и при выходе из поля запрашивается дополнительная информация, а часть формы обновляется. Это показалось, что все работает хорошо.

<h:panelGroup id="clientInfo" layout="block" rendered="#{field.type == 'CLIENTINFO'}">
    <h:outputLabel for="inputClientId">#{field.label}</h:outputLabel>
    <p:inputText id="inputClientId" maxlength="9" value="#{handler.property(field.id).value}">
        <p:ajax listener="#{handler.fetchClientDetails(field.id)}" partialSubmit="true" process="@this" update="@parent,:mainform:msgs"
    </p:inputText>
    <!-- Additional output text elements to display the name, address etc. -->
</h:panelGroup>

Недавно мы добавили поле, которое позволило ввести a java.util.Date с использованием компонента выбора даты PrimeFaces. После добавления этого типа поля частичное обновление перестало работать.

<h:panelGroup id="date" layout="block" rendered="#{field.type == 'DATE'}">
    <h:outputLabel for="inputDate">#{field.label}</h:outputLabel>
    <p:calendar id="inputDate" value="#{handler.property(field.id).valueAsDate}" pattern="dd-MM-yyyy" maxlength="10">
        <f:convertDateTime pattern="dd-MM-yyyy" />
    </p:calendar>
</h:panelGroup>

При проверке частичного результата с сервера мы получили что-то вроде следующего (где 12345 - clientId, введенное в поле выше).

<partial-response>
    <error>
        <error-name>class java.text.ParseException</error-name>
        <error-message><![CDATA[Unparseable date: "12345"]]></error-message>
    </error>
</partial-response>

Вопрос в том, почему он даже вызывает метод getValueAsDate для поля, не являющегося Date, или когда на экране даже нет типа поля даты? Вероятно, это то, что мне не хватает (или недоразумение) о жизненном цикле JSF или о том, как частичные обновления работают в JSF/PrimeFaces.

Обновление # 1:

Только что заметил в другом сеансе отладки, что это происходит не только для частичного обновления, но уже при первоначальном рендеринге экрана. Кажется, что все EL-выражения все время оцениваются, это также приводит к дополнительным свойствам в моем объекте backing (когда запрашивается свойство и оно не существует, оно создается со значением null).

Обновление # 2:

Код для отображения сконфигурированных полей использует ui:repeat en conditional ui:fragment (также попытался h:panelGroup), чтобы отобразить конкретный элемент ввода для сконфигурированного поля.

<ui:repeat value=#{handler.formFields} var="field">
    <ui:fragment rendered="field.type == 'DATE'>
        <!-- Specific fragment for date field -->
    </ui:fragment>
    <ui:fragment rendered="field.type == 'TEXT'>
    </ui:fragment>
    <ui:fragment rendered="field.type == 'REGEXP'>
    </ui:fragment>
    <ui:fragment rendered="field.type == 'CLIENT'>
    </ui:fragment>
</ui:repeat>

Пробовал как h:panelGroup, так и ui:fragments комбинации 2.

Ответ 1

Это распознается как специфическая ошибка Mojarra <ui:repeat>, о которой сообщалось как проблема 3215, исправленная в 2.2.7 и обратная в 2.1.29 по issue 3221. Проще говоря, проблема сводится к тому, что <ui:repeat> не соблюдает состояние своих детей EditableValueHolder при сохранении собственного состояния, тем самым в основном ведет себя так, как будто атрибут rendered этих детей никогда не соблюдался во время сохранения состояния. Одно из других последствий заключается в следующем связанном Q & A: PropertyNotFoundException при условно визуализированных подклассах в ui: repeat.

Учитывая, что вы на Mojarra 2.1.x, лучше всего обновить до 2.1.29. Если ваша среда позволяет это (Servlet 3.0 и т.д.), Возможно также обновление до версии 2.2.x.

Альтернативы будут заменять Mojarra на MyFaces или заменить <ui:repeat><ui:fragment> на <c:forEach><c:if>.