JSF2 Facelets中的JSTL ...有意义吗? [英] JSTL in JSF2 Facelets... makes sense?

查看:85
本文介绍了JSF2 Facelets中的JSTL ...有意义吗?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I would like to output a bit of Facelets code conditionally.

为此,JSTL标记似乎可以正常工作:

For that purpose, the JSTL tags seem to work fine:

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

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

However, I'm not sure if this is a best practice? Is there another way to achieve my goal?

推荐答案

简介

JSTL <c:xxx>标签都是 taghandlers ,它们在视图构建时间期间执行,而JSF <h:xxx>标记都是

Introduction

JSTL <c:xxx> tags are all taghandlers and they are executed during view build time, while JSF <h:xxx> tags are all UI components and they are executed during view render time.

请注意,在JSF自己的<f:xxx><ui:xxx>标记中,只有那些 not 无效的标记才从

Note that from JSF's own <f:xxx> and <ui:xxx> tags only those which do not extend from UIComponent are also taghandlers, e.g. <f:validator>, <ui:include>, <ui:define>, etc. The ones which extend from UIComponent are also JSF UI components, e.g. <f:param>, <ui:fragment>, <ui:repeat>, etc. From JSF UI components only the id and binding attributes are also evaluated during view build time. Thus the below answer as to JSTL lifecycle also applies to the id and binding attributes of JSF components.

视图构建时间是解析XHTML/JSP文件并将其转换为JSF组件树的那一刻,然后将其存储为FacesContextUIViewRoot.视图渲染时间是指从UIViewRoot#encodeAll()开始的JSF组件树即将生成HTML的那一刻.因此:JSF UI组件和JSTL标记不会像您期望的那样同步运行.您可以将其可视化如下:JSTL首先从上到下运行,生成JSF组件树,然后轮到JSF再次从上到下运行,生成HTML输出.

The view build time is that moment when the XHTML/JSP file is to be parsed and converted to a JSF component tree which is then stored as UIViewRoot of the FacesContext. The view render time is that moment when the JSF component tree is about to generate HTML, starting with UIViewRoot#encodeAll(). So: JSF UI components and JSTL tags doesn't run in sync as you'd expect from the coding. You can visualize it as follows: JSTL runs from top to bottom first, producing the JSF component tree, then it's JSF's turn to run from top to bottom again, producing the HTML output.

例如,此Facelets标记使用<c:forEach>迭代了3个项目:

For example, this Facelets markup iterating over 3 items using <c:forEach>:

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

...在视图构建期间,在JSF组件树中创建三个单独的<h:outputText>组件,大致表示如下:

...creates during view build time three separate <h:outputText> components in the JSF component tree, roughly represented like this:

<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输出:

...which in turn individually generate their HTML output during view render time:

<span id="item_1">value1</span>
<span id="item_2">value2</span>
<span id="item_3">value3</span>

请注意,您需要手动确保组件ID的唯一性,并且还在视图构建期间对其进行评估.

Note that you need to manually ensure the uniqueness of the component IDs and that those are also evaluated during view build time.

此Facelets标记使用JSF UI组件<ui:repeat>迭代了3个项目:

While this Facelets markup iterating over 3 items using <ui:repeat>, which is a JSF UI component:

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

...已经按原样结束在JSF组件树中,由此重用时,相同的<h:outputText>组件在视图渲染期间将根据当前迭代回合生成HTML输出:

...already ends up as-is in the JSF component tree whereby the very same <h:outputText> component is during view render time being reused to generate HTML output based on current iteration round:

<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,因为它也在视图构建期间进行评估,而#{item}仅在视图渲染期间可用. h:dataTable和类似组件也是如此.

Note that the <ui:repeat> as being a NamingContainer component already ensured the uniqueness of the client ID based on iteration index; it's also not possible to use EL in id attribute of child components this way as it is also evaluated during view build time while #{item} is only available during view render time. Same is true for an h:dataTable and similar components.

再举一个例子,这个Facelets标记有条件地使用<c:if>添加了不同的标签(您也可以使用<c:choose><c:when><c:otherwise>):

As another example, this Facelets markup conditionally adding different tags using <c:if> (you can also use <c:choose><c:when><c:otherwise> for this):

<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组件树中:

...will in case of type = TEXT only add the <h:inputText> component to the JSF component tree:

<h:inputText ... />

此Facelets标记:

While this Facelets markup:

<h:inputText ... rendered="#{field.type eq 'TEXT'}" />
<h:inputSecret ... rendered="#{field.type eq 'PASSWORD'}" />
<h:selectOneMenu ... rendered="#{field.type eq 'SELECTONE'}" />

...无论情况如何,最终将完全按照上述方法在JSF组件树中结束.因此,当您拥有许多组件树并且它们实际上是基于静态"模型时(即field至少在视图范围内从未更改),这可能最终会变成膨胀"的组件树.另外,在处理子类时,您可能会遇到EL 麻烦在2.2.7之前的Mojarra版本中具有其他属性.

...will end up exactly as above in the JSF component tree regardless of the condition. This may thus end up in a "bloated" component tree when you have many of them and they are actually based on a "static" model (i.e. the field does not ever change during at least the view scope). Also, you may run into EL trouble when you deal with subclasses with additional properties in Mojarra versions before 2.2.7.

它们不可互换. <c:set>在EL作用域中设置一个变量,该变量只能在视图构建期间在标签位置的之后访问,而在视图渲染期间只能在视图中的任何位置访问. <ui:param>将EL变量传递给通过<ui:include><ui:decorate template><ui:composition template>包含的Facelet模板.较早的JSF版本存在一些错误,该错误使<ui:param>变量也可以在所涉及的Facelet模板之外使用,因此永远不要依赖于此.

They are not interchangeable. The <c:set> sets a variable in the EL scope, which is accessible only after the tag location during view build time, but anywhere in the view during view render time. The <ui:param> passes an EL variable to a Facelet template included via <ui:include>, <ui:decorate template>, or <ui:composition template>. Older JSF versions had bugs whereby the <ui:param> variable is also available outside the Facelet template in question, this should never be relied upon.

没有scope属性的<c:set>的行为类似于别名.它不会在任何范围内缓存EL表达式的结果.因此,它可以完美地用于内部,例如迭代JSF组件.因此,例如以下将正常工作:

The <c:set> without a scope attribute will behave like an alias. It does not cache the result of the EL expression in any scope. It can thus perfectly fine be used inside for example iterating JSF components. Thus, e.g. below will work fine:

<ui:repeat value="#{bean.products}" var="product">
    <c:set var="price" value="#{product.price}" />
    <h:outputText value="#{price}" />
</ui:repeat>

仅不适用于循环计算总和.为此,请使用 EL 3.0流:

It's only not suitable for e.g. calculating the sum in a loop. For that instead use 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属性设置为允许值requestviewsessionapplication之一时,它将在视图构建期间立即进行评估并存储在指定的值中.范围.

Only, when you set the scope attribute with one of allowable values request, view, session, or application, then it will be evaluated immediately during view build time and stored in the specified scope.

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

这将只评估一次,并在整个应用程序中以#{dev}形式提供.

This will be evaluated only once and available as #{dev} throughout the entire application.

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

Using JSTL may only lead to unexpected results when being used inside JSF iterating components such as <h:dataTable>, <ui:repeat>, etc, or when JSTL tag attributes depend on results of JSF events such as preRenderView or submitted form values in the model which aren't available during view build time. So, use JSTL tags only to control flow of JSF component tree building. Use JSF UI components to control flow of HTML output generation. Do not bind the var of iterating JSF components to JSTL tag attributes. Do not rely on JSF events in JSTL tag attributes.

任何时候,您认为需要通过binding将组件绑定到支持bean,或通过findComponent()抓取一个组件,并使用Java代码在带有new SomeComponent()的支持bean中创建/操纵其子对象,而没有,那么您应该立即停止并考虑改用JSTL.由于JSTL也是基于XML的,因此动态创建JSF组件所需的代码将变得更加易于阅读和维护.

Anytime you think you need to bind a component to the backing bean via binding, or grab one via findComponent(), and create/manipulate its children using Java code in a backing bean with new SomeComponent() and what not, then you should immediately stop and consider using JSTL instead. As JSTL is also XML based, the code needed to dynamically create JSF components will become so much better readable and maintainable.

重要的是,当在JSTL标记属性中引用视图作用域的bean时,Mojarra的2.1.18版之前的版本存在部分状态保存错误.将重新创建整个视图作用域的bean,而不是从视图树中检索()(这是因为在JSTL运行时尚无法使用完整的视图树).如果您期望通过JSTL标签属性在视图范围内的bean中存储或存储某些状态,则它将不会返回您期望的值,否则它将在实际视图范围内的bean中丢失",该bean在视图之后被还原树被建立.如果您无法升级到Mojarra 2.1.18或更高版本,解决方法是关闭web.xml中的部分状态保存,如下所示:

Important to know is that Mojarra versions older than 2.1.18 had a bug in partial state saving when referencing a view scoped bean in a JSTL tag attribute. The whole view scoped bean would be newly recreated instead of retrieved from the view tree (simply because the complete view tree isn't available yet at the point JSTL runs). If you're expecting or storing some state in the view scoped bean by a JSTL tag attribute, then it won't return the value you expect, or it will be "lost" in the real view scoped bean which is restored after the view tree is built. In case you can't upgrade to Mojarra 2.1.18 or newer, the work around is to turn off partial state saving in web.xml like below:

<context-param>
    <param-name>javax.faces.PARTIAL_STATE_SAVING</param-name>
    <param-value>false</param-value>
</context-param>

另请参见:

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆