JSF Условно включает, потому что ID компонента уже был найден в представлении

Я знаю, что мы не можем повторить идентификатор любого компонента, который мы имеем в том же дереве представления.

У меня есть страница, которая включает в себя другие страницы по определенным условиям, как это...

<h:panelGroup rendered="#{bean.insertMode == 'SINGLE'}">
   <ui:include src="_single.xhtml" />
</h:panelGroup> 
<h:panelGroup rendered="#{bean.insertMode == 'DOUBLE'}">
   <ui:include src="_double.xhtml" />
</h:panelGroup>

Теперь на этих страницах у меня есть "Почти" одна и та же иерархия компонентов (сложная) с различным поведением действий (не только вызовы методов, но и представление), например:

_single.xhtml

<p:inputText id="fieldID" value="#{bean.value}" />
<p:commandLink actionListener="#{bean.singleAction()}" />

_double.xhtml

<p:inputText id="fieldID" value="#{bean.value}" />
<p:commandLink actionListener="#{bean.doubleAction()}" />

Мой маленький пример работает отлично, и рендерится как положено, но я получаю

java.lang.IllegalStateException: Component ID fieldID has already been found in the view.

Я знаю, что JSF обрабатывает полные страницы, даже если они не включены, и именно поэтому я получаю это исключение.

Любой разумный способ решить эту проблему без изменения идентификаторов компонентов внутри включаемых страниц (хотя это работает, но исключение раздражает и кажется, что что-то не так).

Я не хочу также обернуть каждую страницу некоторым контейнерным компонентом с другим идентификатором, чтобы у них был другой полный идентификатор, такой как formId:fieldID, потому что главная страница также ссылается на эти компоненты внутри этих включений!

1 ответ

Решение

Произошла ошибка идентификатора дубликата компонента, поскольку оба они физически оказались в дереве компонентов JSF. <h:panelGroup rendered="false"> не препятствует тому, чтобы они попали в дерево компонентов JSF, вместо этого он предотвращает генерацию их вывода HTML.

Вместо условного рендеринга их вывода HTML, вам необходимо условно построить их в дереве компонентов JSF. JSTL очень полезен в этом, поскольку он работает во время сборки представления:

<c:if test="#{bean.insertMode eq 'SINGLE'}">
    <ui:include src="_single.xhtml" />
</c:if> 
<c:if test="#{bean.insertMode eq 'DOUBLE'}">
    <ui:include src="_double.xhtml" />
</c:if>

В случае, если вы используете Mojarra, вам нужно только убедиться, что вы используете по крайней мере версию 2.1.18 или новее, в противном случае bean-компоненты вида scoped будут вести себя как bean-объекты запроса.

Альтернативой является использование условного оператора EL в src атрибут (<ui:include> сам работает как обработчик тегов также во время построения представления):

<ui:include src="_#{bean.insertMode eq 'SINGLE' ? 'single' : 'double'}.xhtml" />

Или даже использовать insertMode прямо как имя файла:

<ui:include src="_#{fn:toLowerCase(bean.insertMode)}.xhtml" />

В любом случае, вы должны быть абсолютно уверены, что #{bean.insertMode} доступно во время построения представления, а также то, что во время фазы восстановления обратных передач доступно то же значение, что и во время начального рендеринга, в противном случае представление может быть восстановлено с неправильным включением, а JSF не сможет декодировать правильные входные данные и командуй больше. Кроме того, когда вы хотите изменить включение во время обратной передачи, вам действительно нужно перестроить представление (вернутьnull/void) или отправить редирект.

Смотрите также:

Другие вопросы по тегам