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
) или отправить редирект.