JSTL в JSF2 Facelets ... имеет смысл? - PullRequest
157 голосов
/ 27 июля 2010

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

Для этой цели теги JSTL работают нормально:

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

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

Ответы [ 3 ]

304 голосов
/ 27 июля 2010

Введение

Все теги

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

Обратите внимание, что из собственных тегов <f:xxx> и <ui:xxx> JSF только те, которые не расширяются от UIComponent, также являются обработчиками тегов, например, <f:validator>, <ui:include>, <ui:define> и т. Д. Те, которые начинаются с UIComponent, также являются компонентами пользовательского интерфейса JSF, например, <f:param>, <ui:fragment>, <ui:repeat> и т. Д. Из компонентов пользовательского интерфейса JSF только атрибуты id и binding также оцениваются во время построения представления. Таким образом, приведенный ниже ответ относительно жизненного цикла JSTL также применим к атрибутам id и binding компонентов JSF.

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

<c:forEach> против <ui:repeat>

Например, эта разметка Facelets итерирует по 3 элементам, используя <c:forEach>:

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

... создает во время построения представления три отдельных <h:outputText> компонента в дереве компонентов 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 элементам, используя <ui:repeat>, который является компонентом пользовательского интерфейса JSF:

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

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

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

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

<c:if> / <c:choose> против rendered

В качестве другого примера, эта разметка Facelets условно добавляет различные теги, используя <c:if> (вы также можете использовать <c:choose><c:when><c:otherwise> для этого):

<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 добавит только компонент <h:inputText> в дерево компонентов 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 независимо от условия. Таким образом, это может оказаться в «раздутом» дереве компонентов, когда у вас их много, и они фактически основаны на «статической» модели (то есть field никогда не меняется, по крайней мере, в области видимости). Кроме того, вы можете столкнуться с проблемой EL неприятности , когда имеете дело с подклассами с дополнительными свойствами в версиях Mojarra до 2.2.7.

<c:set> против <ui:param>

Они не взаимозаменяемы. <c:set> устанавливает переменную в области видимости EL, которая доступна только после расположения тега во время построения представления, но в любом месте представления во время визуализации представления. <ui:param> передает переменную EL в шаблон Facelet, включенный через <ui:include>, <ui:decorate template> или <ui:composition template>. В старых версиях JSF были ошибки, из-за которых переменная <ui:param> также доступна за пределами рассматриваемого шаблона Facelet, на это никогда не следует полагаться.

<c:set> без атрибута 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 stream :

<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, таких как <h:dataTable>, <ui:repeat> и т. Д., Или когда атрибуты тегов JSTLзависит от результатов событий JSF, таких как preRenderView или отправленных значений формы в модели, которые недоступны во время построения представления.Таким образом, используйте теги JSTL только для управления потоком построения дерева компонентов JSF.Используйте компоненты пользовательского интерфейса JSF для управления потоком генерации вывода HTML.Не связывайте var итерации компонентов JSF с атрибутами тегов JSTL.Не полагайтесь на события JSF в атрибутах тегов JSTL.

Каждый раз, когда вы думаете, что вам нужно связать компонент с компонентом поддержки с помощью binding, или захватить один с помощью findComponent(), и создавать / манипулировать его дочерними элементами, используяJava-код в компоненте поддержки с 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вместо этого в HTML-компоненте JSF , в частности , если #{lpc} представляет текущий итеративный элемент итеративного компонента JSF, такой как <h:dataTable> или <ui:repeat>.

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

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

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

См. Также:

13 голосов
/ 27 июля 2010

использование

<h:panelGroup rendered="#{lpc.verbose}">
  ...
</h:panelGroup>
4 голосов
/ 24 декабря 2013

Извините за отдельный ответ, но я не смог прокомментировать ответы выше.

Для вывода с переключением можно использовать переключатель из primefaces-extensions .

...