Я хотел бы вывести немного кода Facelets условно.
Для этого теги JSTL работают нормально:
<c:if test="${lpc.verbose}">
...
</c:if>
Тем не менее, я не уверен, что это лучшая практика? Есть ли другой способ достичь моей цели?
Все <c:xxx>
теги JSTL являются обработчиками тегов и выполняются во время построения представления , а <h:xxx>
теги JSF - все компоненты пользовательского интерфейса и выполняются во время визуализации представления .
Обратите внимание , что из собственных JSF, <f:xxx>
и <ui:xxx>
тегов только те , у которых не простираются от UIComponent
также taghandlers, например <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 :
<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, таких как <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>
@ViewScoped
не работает в обработчиках теговЧтобы увидеть примеры из реальной жизни, в которых полезны теги 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>
<ui:repeat>
является обработчиком тега (из-за этой строки « Обратите внимание, что JSF принадлежит<f:xxx>
и<ui:xxx>
... ») точно так же,<c:forEach>
и, следовательно, он оценивается во время построения представления (опять же, точно так же, как просто<c:forEach>
) , Если это так, то не должно быть никакой видимой функциональной разницы между<ui:repeat>
и<c:forEach>
? Я не понимаю, что именно означает этот абзац :)<f:xxx>
и<ui:xxx>
теги, которые не расширяютсяUIComponent
, также являются обработчиками тегов ». Попытки предположить, что<ui:repeat>
это также обработчик тегов, потому что<ui:xxx>
также включает в себя<ui:repeat>
? Это должно означать, что<ui:repeat>
это один из компонентов,<ui:xxx>
который расширяетUIComponent
. Следовательно, это не обработчик тега. (Некоторые из них могут не расширятьсяUIComponent
. Следовательно, они являются обработчиками тегов) Так ли это?<c:set>
безscope
создает псевдоним выражения EL вместо установки оцененного значения в целевой области.scope="request"
Вместо этого попробуйте , который сразу оценит значение (действительно во время сборки представления) и установит его как атрибут запроса (который не будет «перезаписан» во время итерации). Под одеялом он создает и устанавливаетValueExpression
объект.ClassNotFoundException
. Временные зависимости вашего проекта нарушены. Скорее всего, вы используете не JavaEE-сервер, такой как Tomcat, и забыли установить JSTL, или вы случайно включили JSTL 1.0 и JSTL 1.1+. Потому что в JSTL 1.0 пакет есть,javax.servlet.jstl.core.*
а с JSTL 1.1 это сталоjavax.servlet.jsp.jstl.core.*
. Подсказки для установки JSTL можно найти здесь: stackoverflow.com/a/4928309использование
источник
Для вывода, подобного переключателю, вы можете использовать поверхность переключателя из расширений PrimeFaces.
источник