JSF2 Facelets中的JSTL有意义吗?

Jan*_*Jan 159 jsf jstl facelets jsf-2

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

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

<c:if test="${lpc.verbose}">
    ...
</c:if>
Run Code Online (Sandbox Code Playgroud)

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

Bal*_*usC 313

介绍

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标记使用<c:forEach>以下内容迭代3个项目:

<c:forEach items="#{bean.items}" var="item">
    <h:outputText id="item_#{item.id}" value="#{item.value}" />
</c:forEach>
Run Code Online (Sandbox Code Playgroud)

...在视图构建期间创建<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}" />
Run Code Online (Sandbox Code Playgroud)

...在视图渲染时间内依次单独生成HTML输出:

<span id="item_1">value1</span>
<span id="item_2">value2</span>
<span id="item_3">value3</span>
Run Code Online (Sandbox Code Playgroud)

请注意,您需要手动确保组件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>
Run Code Online (Sandbox Code Playgroud)

...已经在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>
Run Code Online (Sandbox Code Playgroud)

注意,<ui:repeat>作为NamingContainer组件已经基于迭代索引确保了客户端ID的唯一性; 也不可能以id这种方式在子组件的属性中使用EL,因为它也在视图构建期间进行评估,而#{item}仅在视图渲染时间期间可用.对于h:dataTable类似的组件也是如此.

<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>
Run Code Online (Sandbox Code Playgroud)

... type = TEXT仅在将<h:inputText>组件添加到JSF组件树的情况下:

<h:inputText ... />
Run Code Online (Sandbox Code Playgroud)

虽然这个Facelets标记:

<h:inputText ... rendered="#{field.type eq 'TEXT'}" />
<h:inputSecret ... rendered="#{field.type eq 'PASSWORD'}" />
<h:selectOneMenu ... rendered="#{field.type eq 'SELECTONE'}" />
Run Code Online (Sandbox Code Playgroud)

...无论条件如何,都将在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>
Run Code Online (Sandbox Code Playgroud)

它仅适用于例如计算循环中的和.为此,使用EL 3.0流:

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

只是,当你设置scope有允许值的一个属性request,view,session,或application,那么它会立即在视图生成时评估,并存储在指定的范围内.

<c:set var="dev" value="#{facesContext.application.projectStage eq 'Development'}" scope="application" />
Run Code Online (Sandbox Code Playgroud)

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

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

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

任何时候你认为你需要通过binding一个通道绑定一个组件到一个支持bean ,或者findComponent()在一个支持bean中使用Java代码创建/操作它的子代new SomeComponent(),然后你应该立即停止并考虑使用JSTL.由于JSTL也是基于XML的,因此动态创建JSF组件所需的代码将变得更易读和可维护.

重要的是要知道,当引用JSTL标记属性中的视图范围bean时,早于2.1.18的Mojarra版本在部分状态保存方面存在错误.整个视图范围的bean将新的重建,而不是从视图树(仅仅是因为完整视图树还没有提供在点JSTL运行)检索.如果您期望或通过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>
Run Code Online (Sandbox Code Playgroud)

也可以看看:

要查看JSTL标记有用的一些真实世界示例(即在构建视图时真正正确使用),请参阅以下问题/答案:


简而言之

至于具体的功能需求,如果要有条件地呈现 JSF组件,请改用renderedJSF HTML组件上的属性,特别是如果#{lpc}表示JSF迭代组件的当前迭代项,如<h:dataTable><ui:repeat>.

<h:someComponent rendered="#{lpc.verbose}">
    ...
</h:someComponent>
Run Code Online (Sandbox Code Playgroud)

或者,如果您想有条件地构建(创建/添加)JSF组件,那么继续使用JSTL.这比new SomeComponent()在java中冗长的做法要好得多.

<c:if test="#{lpc.verbose}">
    <h:someComponent>
        ...
    </h:someComponent>
</c:if>
Run Code Online (Sandbox Code Playgroud)

也可以看看:

  • @Aklin:不是吗?那个[这个例子](http://stackoverflow.com/questions/3510614/how-to-create-dynamic-jsf-1-2-form-fields/3522489#3522489)怎么样? (3认同)
  • @Shirgill:`<c:set>`没有`scope`创建EL表达式的别名,而不是在目标范围内设置评估值.请尝试`scope ="request"`,它将立即评估该值(确实在视图构建期间)并将其设置为请求属性(在迭代期间不会被"覆盖").在封面下,它创建并设置一个`ValueExpression`对象. (2认同)

Boz*_*zho 13

使用

<h:panelGroup rendered="#{lpc.verbose}">
  ...
</h:panelGroup>
Run Code Online (Sandbox Code Playgroud)

  • 使用h:panelGroup是一个肮脏的解决方案,因为它会生成一个&lt;span&gt;标记,而c:if则不会向html代码添加任何内容。h:panelGroup在panelGrids内部也是有问题的,因为它对元素进行了分组。 (3认同)

归档时间:

查看次数:

82125 次

最近记录:

7 年,5 月 前