JSTL в JSF2 Facelets … имеет смысл?

Я хотел бы произвести немного кода Facelets условно.

С этой целью теги JSTL, кажется, хорошо работают:

<c:if test="${lpc.verbose}">
    ...
</c:if>

Однако я не уверен, является ли это лучшей практикой? Там другой путь состоит в том, чтобы достигнуть моей цели?

159
задан BalusC 22 March 2017 в 08:49
поделиться

2 ответа

Введение

Теги JSTL - это все обработчики тегов , и они выполняются во время времени построения представления , в то время как JSF < h: xxx> теги - это все компоненты пользовательского интерфейса , и они выполняются во время времени рендеринга .

Обратите внимание, что из собственных тегов JSF и только те, которые не , а не , наследуются от UIComponent также являются обработчиками тегов, например , , и т. Д. Те, которые происходят от UIComponent также являются компонентами пользовательского интерфейса JSF, например , , и т. Д. Из компонентов пользовательского интерфейса JSF только идентификатор ] и атрибуты привязки также оцениваются во время построения представления. Таким образом, приведенный ниже ответ относительно жизненного цикла JSTL также применим к атрибутам id и binding компонентов JSF.

Время построения представления - это момент, когда файл XHTML / JSP должен быть проанализирован и преобразован в дерево компонентов JSF, которое затем сохраняется как UIViewRoot из FacesContext . Время визуализации представления - это момент, когда дерево компонентов JSF собирается сгенерировать HTML, начиная с UIViewRoot # encodeAll () . Итак: компоненты пользовательского интерфейса JSF и теги JSTL не работают синхронно, как можно было бы ожидать от кодирования.Вы можете визуализировать это следующим образом: JSTL сначала запускается сверху вниз, создавая дерево компонентов JSF, затем очередь JSF снова запускаться сверху вниз, создавая вывод HTML.

vs

Например, эта разметка Facelets повторяет 3 элемента с использованием :

<c:forEach items="#{bean.items}" var="item">
    <h:outputText id="item_#{item.id}" value="#{item.value}" />
</c:forEach>

. ..создает во время сборки представления три отдельных компонента в дереве компонентов JSF, примерно представленных следующим образом:

<h:outputText id="item_1" value="#{bean.items[0].value}" />
<h:outputText id="item_2" value="#{bean.items[1].value}" />
<h:outputText id="item_3" value="#{bean.items[2].value}" />

... которые, в свою очередь, индивидуально генерируют свой HTML-вывод во время визуализации представления :

<span id="item_1">value1</span>
<span id="item_2">value2</span>
<span id="item_3">value3</span>

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

Хотя эта разметка Facelets повторяет 3 элемента с использованием , который является компонентом пользовательского интерфейса JSF:

<ui:repeat id="items" value="#{bean.items}" var="item">
    <h:outputText id="item" value="#{item.value}" />
</ui:repeat>

... уже заканчивается как есть в дереве компонентов JSF, в результате чего тот же компонент во время визуализации представления повторно используется для генерации вывода HTML на основе текущего раунда итерации:

<span id="items:0:item">value1</span>
<span id="items:1:item">value2</span>
<span id="items:2:item">value3</span>

Обратите внимание, что как компонент NamingContainer уже обеспечил уникальность идентификатора клиента на основе индекса итерации; также невозможно использовать EL в атрибуте id дочерних компонентов таким образом, поскольку он также оценивается во время построения представления, в то время как # {item} доступен только во время визуализации представления. То же самое верно для h: dataTable и подобных компонентов.

/ vs rendered

В качестве другого примера, эта разметка Facelets условно добавляет разные теги с помощью (вы также можете использовать для этого):

<c:if test="#{field.type eq 'TEXT'}">
    <h:inputText ... />
</c:if>
<c:if test="#{field.type eq 'PASSWORD'}">
    <h:inputSecret ... />
</c:if>
<c:if test="#{field.type eq 'SELECTONE'}">
    <h:selectOneMenu ... />
</c:if>

... будет в случае type = TEXT добавляет только компонент в дерево компонентов JSF:

<h:inputText ... />

В то время как эта разметка Facelets:

<h:inputText ... rendered="#{field.type eq 'TEXT'}" />
<h:inputSecret ... rendered="#{field.type eq 'PASSWORD'}" />
<h:selectOneMenu ... rendered="#{field.type eq 'SELECTONE'}" />

... в конечном итоге будет точно так же, как указано выше в дереве компонентов JSF, независимо от состояния. Таким образом, это может привести к «раздутому» дереву компонентов, когда у вас их много, и они фактически основаны на «статической» модели (то есть поле никогда не изменяется, по крайней мере, во время просмотра) . Кроме того, вы можете столкнуться с проблемой EL , когда имеете дело с подклассами с дополнительными свойствами в версиях Mojarra до 2.2.7.

vs

Они не взаимозаменяемы. устанавливает переменную в области EL, которая доступна только после местоположения тега во время построения представления, но в любом месте представления во время визуализации представления. передает переменную EL в шаблон Facelet, включенный через , или ] . В более старых версиях JSF были ошибки, из-за которых переменная также была доступна за пределами рассматриваемого шаблона Facelet, на это никогда не следует полагаться.

без атрибута scope будет вести себя как псевдоним. Он не кэширует результат выражения EL ни в какой области. Таким образом, его можно прекрасно использовать, например, для итерации компонентов JSF. Таким образом, например, ниже будет работать нормально:

<ui:repeat value="#{bean.products}" var="product">
    <c:set var="price" value="#{product.price}" />
    <h:outputText value="#{price}" />
</ui:repeat>

Это только не подходит, например, для подсчет суммы в цикле. Для этого используйте поток EL 3.0 : Только

<ui:repeat value="#{bean.products}" var="product">
    ...
</ui:repeat>
<p>Total price: #{bean.products.stream().map(product->product.price).sum()}</p>

,когда вы устанавливаете атрибут scope с одним из допустимых значений request , view , session или application , тогда он будет немедленно оценен во время сборки представления и сохранен в указанной области.

<c:set var="dev" value="#{facesContext.application.projectStage eq 'Development'}" scope="application" />

Это будет оцениваться только один раз и доступно как # {dev} во всем приложении.

Использование JSTL для управления построением дерева компонентов JSF

Использование JSTL может привести только к неожиданным результатам при использовании внутри итерационных компонентов JSF, таких как , и т. Д., Или когда атрибуты тега JSTL зависят от результатов событий JSF, таких как preRenderView , или от представленных значений формы в модели, которые недоступны во время построения представления. Поэтому используйте теги JSTL только для управления процессом построения дерева компонентов JSF. Используйте компоненты пользовательского интерфейса JSF для управления потоком создания вывода HTML. Не привязывайте var повторяющихся компонентов JSF к атрибутам тега JSTL. Не полагайтесь на события JSF в атрибутах тега JSTL.

Каждый раз, когда вы думаете, что вам нужно привязать компонент к поддерживающему bean-компоненту с помощью binding , или получить его с помощью findComponent () , и создать / управлять его дочерними элементами, используя код Java в поддерживая bean-компонент с помощью new SomeComponent () и т.д., тогда вам следует немедленно остановиться и рассмотреть возможность использования JSTL. Поскольку JSTL также основан на XML, код, необходимый для динамического создания компонентов JSF, станет намного удобнее для чтения и сопровождения.

Важно знать, что версии Mojarra старше 2.1.18 имелась ошибка частичного сохранения состояния при ссылке на bean-компонент с областью просмотра в атрибуте тега JSTL. Компонент с областью видимости будет заново воссоздан вместо того, чтобы извлекаться из дерева представления (просто потому, что полное дерево представления еще не доступно на момент запуска JSTL). Если вы ожидаете или сохраняете какое-то состояние в bean-компоненте с областью просмотра с помощью атрибута тега JSTL, то он не вернет ожидаемое значение или будет «потерян» в bean-компоненте с реальной областью просмотра, который восстанавливается после представления. дерево построено. Если вы не можете выполнить обновление до Mojarra 2.1.18 или новее, попробуйте отключить частичное сохранение состояния в web.xml , как показано ниже:

<context-param>
    <param-name>javax.faces.PARTIAL_STATE_SAVING</param-name>
    <param-value>false</param-value>
</context-param>

См. Также:

Чтобы увидеть некоторые реальные примеры использования тегов JSTL (т. е.при правильном использовании во время построения представления) см. следующие вопросы / ответы:


Вкратце

Что касается ваших конкретных функциональных требований, если вы хотите визуализировать компоненты JSF условно, используйте вместо этого атрибут rendered в компоненте JSF HTML , особенно if # {lpc} представляет повторяющийся в данный момент элемент итерационного компонента JSF, например или .

<h:someComponent rendered="#{lpc.verbose}">
    ...
</h:someComponent>

Или, если вы хотите построить (создать / добавить) компоненты JSF условно, продолжайте использовать JSTL. Это намного лучше, чем многословное выполнение new SomeComponent () в java.

<c:if test="#{lpc.verbose}">
    <h:someComponent>
        ...
    </h:someComponent>
</c:if>

См. Также:

313
ответ дан 23 November 2019 в 21:36
поделиться

используйте

<h:panelGroup rendered="#{lpc.verbose}">
  ...
</h:panelGroup>
13
ответ дан 23 November 2019 в 21:36
поделиться
Другие вопросы по тегам:

Похожие вопросы: