【问题标题】:Access iteration variable in passthrough attribute?在传递属性中访问迭代变量?
【发布时间】:2014-03-19 15:06:40
【问题描述】:

我想知道为什么我会在以下内容上收到 NullPointerException

<h:dataTable value="#{testBean.entities}" var="d">
  <h:column p:data-order="#{d.modifiedOn}">
    <f:facet name="header">Date</f:facet>
    <h:outputText value="#{d.modifiedOn}">
      <f:convertDateTime pattern="#{msg.datePattern}" />
    </h:outputText>
  </h:column>    
</h:dataTable>

其中p 命名空间是xmlns:p="http://xmlns.jcp.org/jsf/passthrough"modifiedOn 是一个(非空)Date 字段。

异常如下所示:

Caused by: java.lang.NullPointerException
    at com.sun.faces.renderkit.html_basic.HtmlResponseWriter.getAttributeValue(HtmlResponseWriter.java:1211)
    at com.sun.faces.renderkit.html_basic.HtmlResponseWriter.flushAttributes(HtmlResponseWriter.java:1171)
    at com.sun.faces.renderkit.html_basic.HtmlResponseWriter.closeStartIfNecessary(HtmlResponseWriter.java:1113)
    at com.sun.faces.renderkit.html_basic.HtmlResponseWriter.writeText(HtmlResponseWriter.java:936)
    at com.sun.faces.facelets.el.ELText$ELTextVariable.writeText(ELText.java:240)
    at com.sun.faces.facelets.compiler.TextInstruction.write(TextInstruction.java:85)
    at com.sun.faces.facelets.compiler.UIInstructions.encodeBegin(UIInstructions.java:82)
    at com.sun.faces.renderkit.html_basic.HtmlBasicRenderer.encodeRecursive(HtmlBasicRenderer.java:302)
    at com.sun.faces.renderkit.html_basic.TableRenderer.renderHeader(TableRenderer.java:339)

但是,#{d.modifiedOn} 可以在任何非直通属性(如 headerClass)中使用。任何不引用传递属性中的迭代变量的值也是如此。

我的问题是:这是故意的吗?

这是使用 Wildfly-8.0.0.Final 的 JSF 实现,版本 2.2.5-jbossorg-3。

如果有人想知道:我正在尝试支持 正交数据属性来增强我的表格。

编辑添加:我也尝试过,但得到了相同的例外,将以下内容添加到&lt;h:column&gt;

<f:passThroughAttribute name="data-order" value="#{d.modifiedOn}" />

这显然是由于 JSF 试图在 rowIndex-1 且迭代数据尚不存在的标头上呈现此属性所致。所以我的具体问题有一个破解解决方案:

<h:column p:data-order="#{empty d or empty d.modifiedOn ? '' : d.modifiedOn.time}">
  ...
</h:column>

这仍然会在我的thead 中呈现一个空/无用的data-order 属性。

进一步测试: 更多测试证实了我的怀疑,这只发生在 any 列上的&lt;f:facet name="header"&gt; 上(不一定是具有该属性的那个)。将这些排除在外会破坏我使用该表作为数据表基础的目标,因为这需要一个带有&lt;thead&gt; 的格式良好的表。

【问题讨论】:

    标签: datatables jsf datatable jsf-2.2


    【解决方案1】:

    这是故意的吗?绝对不是。此外,这里是根据 JSF 2.2 规范定义的 passthrough 属性:

    传递属性

    对于视图中的任何给定 JSF 组件标记,组件支持的可用属性集由 该标签的 UIComponent 和 Renderer 的组合。在某些情况下,属性的值被解释为 UIComponentor Renderer(例如,h:panelGrid 的 columns 属性),其他的值为 直接传递给用户代理(例如,h:inputText 的 lang 属性)。

    在这两种情况下, UIComponent/Renderer 具有一组允许属性的先验知识。传递属性允许 视图作者列出任意名称值对,这些名称值对直接传递给用户代理,无需解释 由 UIComponent/Renderer。此行为在“渲染传递属性”中规范地指定 标准 HTML_BASIC 渲染工具包概述部分。

    所以这种属性的唯一功能是通知渲染器它必须处理属性的值,但它不绑定到任何 UIComponent 特性,所以必须以最终输出结束

    我已经使用 Tomcat 7 + Mojarra 2.2.6 进行了测试,它非常适合我。 HTML 元素使用它们的 data-order 属性和给定的日期在 DOM 树中呈现。这是我用过的测试用例:

    <html xmlns:f="http://xmlns.jcp.org/jsf/core"
        xmlns="http://www.w3.org/1999/xhtml"
        xmlns:h="http://xmlns.jcp.org/jsf/html"
        xmlns:p="http://xmlns.jcp.org/jsf/passthrough">
    <h:head />
    <h:body>
        <h:dataTable value="#{bean.entities}" var="entity">
            <h:column p:data-order="#{entity.modifiedOn}">
                #{entity.name}
            </h:column>
        </h:dataTable>
        <h:messages />
    </h:body>
    </html>
    
    @ManagedBean
    @ViewScoped
    public class Bean {
    
        public class Entity {
            private String name;
    
            private Date modifiedOn;
    
            public Entity(String name, Date modifiedOn) {
                this.name = name;
                this.modifiedOn = modifiedOn;
            }
    
            public Date getModifiedOn() {
                return modifiedOn;
            }
    
            public String getName() {
                return name;
            }
    
        }
    
        /**
         * Create a List of entities with dates differing from now to now + 2 days
         */
        public List<Entity> entities = Arrays.asList(
                new Entity("name1", new Date()), new Entity("name2", new Date(
                        new Date().getTime() + (1000 * 60 * 60 * 24))), new Entity(
                        "name0", new Date(new Date().getTime()
                                + (1000 * 60 * 60 * 48))));
    
        public List<Entity> getEntities() {
            return entities;
        }
    
    }
    

    这意味着它是您的 JSF 实现中的一个错误(我不知道 BTW 的实现)。最好打开一个错误跟踪,除非作者已经在更高版本中修复它。

    编辑

    当数据表的页眉或页脚存在f:facet 时,呈现失败并显示给定错误。显然,Mojarra 尝试在构建迭代器之前评估构面的传递值,这会导致它失败,因为变量尚不可用。打开了issue

    另请参阅:

    【讨论】:

    • 我将切换到 Mojarra-2.2.6 并再次测试。另外,我只是在我的原始帖子中暗示了这一点,但是您可以尝试使用&lt;f:facet name="header"&gt;&lt;h:column&gt; 强制生成表头吗?
    • 使用标题方面,它也会在 Mojarra 中引发给定的异常。我推断失败的原因是:在&lt;td&gt;元素中设置了passthrough属性,但是添加列标题时,标题没有&lt;td&gt;,而是&lt;th&gt;。然而,该框架会寻找 &lt;td&gt; 并且由于没有找到它,因此会引发异常。我认为这是一个错误,所以我将在 Mojarra 网站上打开一个问题报告。
    • 谢谢,虽然我同意这至少是出乎意料的,但我认为问题实际上是渲染器试图过早地评估表达式:我将 h:dataTable VDL 解释为暗示顺序和编码标题是在设置rowIndex 之前完成(仅在呈现表体时发生),这将导致无法访问迭代变量。所以,是的,这实际上可能是符合规范的行为。虽然在 NPE 上失败肯定是……出乎意料的。
    • 当涉及到f:facet 时,您的“黑客”将起作用。这意味着您所说的,对于要评估的 EL 表达式,标头编码执行得太早了。显然,Mojarra 首先评估标题,然后创建一个完整的迭代器来构造每一行。问题是在标题渲染时传递上的表达式尚不可用,只是因为尚未定义迭代变量。这会导致问题。
    • Jup,同意我的推理。但我不确定规范人员是否可以做某事(第 4.2.1.1 节(DataModel 属性)明确表示rowData 在不迭代时是null)。如果有解决方案就好了。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-05-16
    • 2022-12-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多