JSF2 Facelets中的JSTL …有意义吗?
我想有条件地输出一些Facelets代码。
为此,JSTL标签似乎工作正常:
<c:if test="${lpc.verbose}"> ... </c:if>
但是,我不确定这是否是最佳做法? 有另一种方法来实现我的目标?
介绍
JSTL <c:xxx>
标记都是标记处理程序 ,它们在视图构build时执行,而JSF <h:xxx>
标记都是UI组件 ,它们在视图渲染时执行。
请注意,从JSF自己的<f:xxx>
和<ui:xxx>
标记中,只有那些未从UIComponent
扩展的标记也是标记处理程序,例如<f:validator>
, <ui:include>
, <ui:define>
等。从UIComponent
扩展的也是JSF UI组件,例如<f:param>
, <ui:fragment>
, <ui:repeat>
等等。从JSF UI组件中,只有id
和binding
属性在view build time 。 因此,JSTL生命周期的下面的答案也适用于JSF组件的id
和binding
属性。
视图构build时间是XHTML / JSP文件被parsing并转换为JSF组件树,然后存储为FacesContext
UIViewRoot
的FacesContext
。 视图呈现时间是JSF组件树即将生成HTML的时刻,从UIViewRoot#encodeAll()
。 因此:JSF UI组件和JSTL标记不会像编码期望的那样同步运行。 您可以按如下方式对其进行可视化:JSTL首先从上到下运行,生成JSF组件树,然后JSF再次从上到下运行,生成HTML输出。
<c:forEach>
vs <ui:repeat>
例如,这个Facelets标记使用<c:forEach>
迭代3个项目:
<c:forEach items="#{bean.items}" var="item"> <h:outputText id="item_#{item.id}" value="#{item.value}" /> </c:forEach>
…在视图构build期间在JSF组件树中创build三个单独的<h:outputText>
组件,大致如下所示:
<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>
请注意,您需要手动确保组件ID的唯一性,并在视图构build时间期间对其进行评估。
虽然这个Facelets标记使用<ui:repeat>
(它是一个JSF UI组件)迭代3个项目:
<ui:repeat id="items" value="#{bean.items}" var="item"> <h:outputText id="item" value="#{item.value}" /> </ui:repeat>
…已经以JSF组件树的forms结束了,因此,在视图渲染时间中,非常相同的<h:outputText>
组件被重用来基于当前迭代生成HTML输出:
<span id="items:0:item">value1</span> <span id="items:1:item">value2</span> <span id="items:2:item">value3</span>
请注意,作为NamingContainer
组件的<ui:repeat>
已经基于迭代索引确保了客户端ID的唯一性; 在这种情况下,也不可能在id
属性中使用EL,因为在视图构build时也会对其进行评估,而#{item}
仅在视图渲染时可用。
<c:if>
/ <c:choose>
vs 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
至less在视图范围内永远不会改变)。 另外,当你在2.2.7之前的Mojarra版本中处理具有附加属性的子类时,你可能遇到EL 麻烦 。
<c:set>
vs <ui:param>
它们不可互换。 <c:set>
在EL范围中设置一个variables,只能在视图构build期间的标签位置之后访问,而在视图渲染期间视图中的任何位置。 <ui:param>
将ELvariables传递给通过<ui:include>
, <ui:decorate template>
或<ui:composition template>
包含的Facelet模板。 较老的JSF版本有错误,即<ui:param>
variables在Facelet模板之外也是可用的,这绝不应该被依赖。
没有scope
属性的<c:set>
将performance得像一个别名。 它不会在任何范围内cachingELexpression式的结果。 因此,它可以很好地用于例如迭代JSF组件。 因此,例如下面将工作正常:
<ui:repeat value="#{bean.products}" var="product"> <c:set var="price" value="#{product.price}" /> <h:outputText value="#{price}" /> </ui:repeat>
它仅适用于例如循环计算总和。 为此而使用EL 3.0stream :
<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
,才会在视图构build时立即对其进行求值并存储在指定范围内。
<c:set var="dev" value="#{facesContext.application.projectStage eq 'Development'}" scope="application" />
这将只被评估一次,并在整个应用程序中作为#{dev}
提供。
使用JSTL来控制JSF组件树的构build
当在JSF迭代组件(如<h:dataTable>
, <ui:repeat>
等)中使用JSTL时,或者当JSTL标记属性依赖于JSF事件的结果(例如preRenderView
或提交的表单值)时,只会导致意外的结果在查看构build时间期间不可用的模型。 因此,仅使用JSTL标签来控制JSF组件树的构buildstream程。 使用JSF UI组件来控制HTML输出生成的stream程。 不要将迭代JSF组件的variables绑定到JSTL标签属性。 不要依赖JSTL标签属性中的JSF事件。
任何时候你认为你需要通过绑定绑定一个组件到backing bean,或者通过findComponent()
获取一个组件,并且使用new SomeComponent()
来创build/操作它的子代在一个backing bean中的Java代码,那么你应该立即停止并考虑使用JSTL。 由于JSTL也是基于XML的,因此dynamic创buildJSF组件所需的代码将变得更加易读易维护。
重要的是要知道,旧版本的Mojarra版本在JSTL标签属性中引用视图范围的bean时,在保存部分状态时存在一个错误。 整个视图范围的bean将被重新创build,而不是从视图树中检索(仅仅因为完整的视图树在JSTL运行的点上还不可用)。 如果您期待或存储视图范围bean中的某个状态由JSTL标记属性,那么它将不会返回您所期望的值,或者它将在视图后恢复的实际视图范围的bean中“丢失”树build成。 如果你不能升级到Mojarra 2.1.18或更新版本,解决方法是closuresweb.xml
部分状态保存,如下所示:
<context-param> <param-name>javax.faces.PARTIAL_STATE_SAVING</param-name> <param-value>false</param-value> </context-param>
也可以看看:
- 什么是视图编译时间?
- “绑定”属性在JSF中如何工作? 何时以及如何使用?
- 如何重构旧的JSP片段到一些JSF的等价物?
- PARTIAL_STATE_SAVING应该设置为false吗?
- JSF 2.0中的通信 –
@ViewScoped
在标记处理程序中失败
要查看JSTL标签有用的一些真实世界示例(即,在构build视图时真正正确使用),请参阅以下问题/答案:
- 如何使JSF复合组件的网格?
- 在JSF中dynamic创build表列
- 如何自定义布局h:selectOneRadio
- JSF中的条件variables定义
- 如何使复合组件类似于<h:selectOneRadio />
- JSF 2 – 在f:ajax上具有可选侦听器属性的组合组件
- 导致堆栈溢出exception的嵌套JSF复合组件
简而言之
至于具体的function需求,如果要有条件地呈现 JSF组件,则应该使用JSF HTML组件上的rendered
属性, 特别是如果#{lpc}
表示JSF迭代组件的当前迭代项,例如<h:dataTable>
或<ui:repeat>
。
<h:someComponent rendered="#{lpc.verbose}"> ... </h:someComponent>
或者,如果要有条件地构build (创build/添加)JSF组件,则继续使用JSTL。 这比在java中使用new SomeComponent()
要好得多。
<c:if test="#{lpc.verbose}"> <h:someComponent> ... </h:someComponent> </c:if>
也可以看看:
- 有条件地显示JSF组件
- JSTL c:如果在JSF h:dataTable中不起作用
- 在<ui:repeat>中指定元素的条件渲染? <c:if>似乎不起作用
使用
<h:panelGroup rendered="#{lpc.verbose}"> ... </h:panelGroup>
对不起,单独的答案,但我不能评论上面的答案。
对于类似开关的输出,您可以使用从primefaces-extensions开关。
- 为什么Facelets比JSF2.0更适合作为视图定义语言呢?
- 在JAR中打包Facelets文件(模板,包含,合成)
- 如何使用本地JavaScript在HTML DOM事件上调用JSF托pipebean?
- 如何使用相同凭据login两次时使用户会话失效
- 从外部文件系统或数据库获取Facelets模板/文件
- 如何在运行时exception被servlet抛出时在浏览器中显示用户友好的错误页面?
- 为什么我需要在另一个组件中使用rendered =“#{some}”嵌套一个组件,当我想要更新它时?
- 如何创builddynamic的JSF表单域
- 如何在CSSselect器中使用带有冒号“:”的JSF生成的HTML元素ID?