JSF2 Facelets中的JSTL ...有意义吗?

内容来源于 Stack Overflow,并遵循CC BY-SA 3.0许可协议进行翻译与使用

  • 回答 (2)
  • 关注 (0)
  • 查看 (39)

我想有条件地输出一些Facelets代码。

为此,JSTL标签似乎工作正常:

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

但是,我不确定这是否是最佳做法?有另一种方法来实现我的目标吗?

提问于
用户回答回答于

介绍

JSTL <c:xxx>标签都是标记处理程序,它们在视图构建期间执行,而JSF <h:xxx>标记都是UI组件,它们在视图渲染时执行。

需要注意的是,从JSF自己<f:xxx><ui:xxx>只有那些标签,其根本没有从延长UIComponent也taghandlers,例如<f:validator><ui:include><ui:define>等从延长的那些UIComponent也JSF UI组件,例如<f:param><ui:fragment><ui:repeat>等从JSF UI组件只idbinding属性也在视图构建时间期间进行评估 因此,下面的答案,JSTL生命周期也适用于idbindingJSF组件的属性。

该视图生成时间是当XHTML / JSP文件是被解析并转化成一个JSF组件树,然后将其存储为那一刻UIViewRootFacesContext。视图呈现时间是JSF组件树即将生成HTML的那一刻,开始于UIViewRoot#encodeAll()。因此:JSF UI组件和JSTL标记不会像编码期望的那样同步运行。您可以如下进行可视化:JSTL首先从上到下运行,生成JSF组件树,然后JSF再次从上到下运行,生成HTML输出。

<c:forEach> VS <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>

请注意,您需要手动确保组件ID的唯一性,并且这些组件ID在查看生成时间期间也会被评估。

虽然这个Facelets标记迭代使用3个项目<ui:repeat>,这是一个JSF UI组件:

<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组件已经基于迭代索引确保了客户端ID的唯一性; id这种方式也不可能在属性中使用EL,因为它也在视图构建时间期间#{item}被评估,而仅在视图渲染时间期间可用。

<c:if>/ <c:choose>vsrendered

作为另一个例子,这个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至少在视图范围内不会改变)时,这可能最终会出现在“臃肿”的组件树中。此外,当您在2.2.7之前处理Mojarra版本中的其他属性的子类时,您可能会遇到EL 故障

<c:set> VS <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有允许值的一个属性requestviewsession,或application,那么它会立即在视图生成时评估,并存储在指定的范围内。

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

这将只被评估一次,并#{dev}在整个应用程序中可用。

使用JSTL来控制JSF组件树构建

在JSF迭代组件(如<h:dataTable><ui:repeat>等)内部使用JSTL时,或者在JSTL标记属性依赖于JSF事件的结果(例如preRenderView模型中提交的表单值或在视图生成期间不可用的表单值)时,使用JSTL可能只会导致意外的结果。因此,仅使用JSTL标记来控制JSF组件树构建的流程。使用JSF UI组件来控制HTML输出生成的流程。不要将var迭代的JSF组件绑定到JSTL标签属性。不要依赖JSTL标签属性中的JSF事件。

无论何时你认为你需要通过绑定一个组件到后台bean上binding,或者通过一个绑定组件来findComponent()创建/操作它的子项new SomeComponent(),你可以立即停止并考虑使用JSTL。由于JSTL也是基于XML的,因此动态创建JSF组件所需的代码将变得更加易读易维护。

重要的是要知道,旧版本的Mojarra版本在JSTL标签属性中引用视图范围的bean时,在保存部分状态时存在一个错误。整个视图范围的bean将新的重建,而不是从视图树(仅仅是因为完整视图树还没有提供在点JSTL运行)检索。如果您期待或通过JSTL标记属性将某些状态存储在视图范围的bean中,则它不会返回您期望的值,或者它将在视图后恢复的实际视图范围的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>

简而言之

至于具体的功能需求,如果要有条件地呈现 JSF组件,请改用renderedJSF HTML组件上的属性,特别是如果#{lpc}代表JSF迭代组件(如<h:dataTable>or)的当前迭代项目<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>

用户回答回答于

使用

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

扫码关注云+社区