如何找到ajax更新/渲染组件的客户端ID?找不到带有从“bar”引用的表达式“foo”的组件

2024-03-14

以下代码受到 PrimeFaces DataGrid + DataTable 教程的启发,并放入<p:tab> of a <p:tabView>居住在一个<p:layoutUnit> of a <p:layout>。这是代码的内部部分(从p:tab成分);外部部分很琐碎。

<p:tabView id="tabs">
    <p:tab id="search" title="Search">                        
        <h:form id="insTable">
            <p:dataTable id="table" var="lndInstrument" value="#{instrumentBean.instruments}">
                <p:column>
                    <p:commandLink id="select" update="insTable:display" oncomplete="dlg.show()">
                        <f:setPropertyActionListener value="#{lndInstrument}" 
                                        target="#{instrumentBean.selectedInstrument}" />
                        <h:outputText value="#{lndInstrument.name}" />
                    </p:commandLink>                                    
                </p:column>
            </p:dataTable>
            <p:dialog id="dlg" modal="true" widgetVar="dlg">
                <h:panelGrid id="display">
                    <h:outputText value="Name:" />
                    <h:outputText value="#{instrumentBean.selectedInstrument.name}" />
                </h:panelGrid>
            </p:dialog>                            
        </h:form>
    </p:tab>
</p:tabView>

当我点击<p:commandLink>,代码停止工作并给出message https://stackoverflow.com/search?q=%22Cannot%20find%20component%20with%20expression%22%20%22referenced%20from%22:

找不到带有从“tabs:insTable:select”引用的表达式“insTable:display”的组件。

当我尝试使用相同的方法时<f:ajax>,然后它会以不同的方式失败message https://stackoverflow.com/search?q=%22contains%20an%20unknown%20id%22%20%22cannot%20locate%20it%20in%20the%20context%20of%20the%20component%22基本上讲的是一样的:

<f:ajax>包含未知 ID“insTable:display”无法在组件“tabs:insTable:select”的上下文中找到它

当它发生在另一个 Ajax 回发期间并且 JSF 项目阶段设置为Development,然后它会失败并显示 JavaScript 警报message https://stackoverflow.com/search?q=malformedXML%20%22during%20update%22%20%22not%20found%22:

malformedXML:更新期间:insTable:找不到显示

这是如何引起的以及如何解决?


在 HTML 输出中查找实际的客户端 ID

您需要查看生成的 HTML 输出以找出正确的客户端 ID。在浏览器中打开页面,右键单击并查看源代码。找到感兴趣的 JSF 组件的 HTML 表示形式并获取其id作为客户端 ID。您可以根据当前的命名容器以绝对或相对方式使用它。参见下一章。

注意:如果它恰好包含迭代索引,例如:0:, :1:等(因为它位于迭代组件内部),那么您需要意识到并不总是支持更新特定迭代轮次。有关更多详细信息,请参阅答案底部。

记住NamingContainer组件并始终给它们一个固定的 ID

如果您想通过 ajax process/execute/update/render 引用的组件位于同一组件内NamingContainer http://docs.oracle.com/javaee/7/api/javax/faces/component/NamingContainer.html父级,然后只需引用它自己的 ID。

<h:form id="form">
    <p:commandLink update="result"> <!-- OK! -->
    <h:panelGroup id="result" />
</h:form>

If it's not里面同一个NamingContainer,那么您需要使用绝对客户端 ID 来引用它。绝对客户端 ID 开头NamingContainer分隔符,默认为:.

<h:form id="form">
    <p:commandLink update="result"> <!-- FAIL! -->
</h:form>
<h:panelGroup id="result" />
<h:form id="form">
    <p:commandLink update=":result"> <!-- OK! -->
</h:form>
<h:panelGroup id="result" />
<h:form id="form">
    <p:commandLink update=":result"> <!-- FAIL! -->
</h:form>
<h:form id="otherform">
    <h:panelGroup id="result" />
</h:form>
<h:form id="form">
    <p:commandLink update=":otherform:result"> <!-- OK! -->
</h:form>
<h:form id="otherform">
    <h:panelGroup id="result" />
</h:form>

NamingContainer http://docs.oracle.com/javaee/7/api/javax/faces/component/NamingContainer.html组件例如<h:form>, <h:dataTable>, <p:tabView>, <cc:implementation>(因此,所有复合组件)等。通过查看生成的 HTML 输出,您可以轻松识别它们,它们的 ID 将被添加到所有子组件生成的客户端 ID 之前。请注意,当它们没有固定 ID 时,JSF 将使用自动生成的 IDj_idXXX格式。您绝对应该通过给他们一个固定的 ID 来避免这种情况。这OmniFacesNoAutoGeneratedIdViewHandler http://showcase.omnifaces.org/viewhandlers/NoAutoGeneratedIdViewHandler在开发过程中可能对此有所帮助。

如果你知道找到javadocUIComponent有问题,那么你也可以在那里检查它是否实现了NamingContainer http://docs.oracle.com/javaee/7/api/javax/faces/component/NamingContainer.html接口与否。例如,HtmlForm http://docs.oracle.com/javaee/7/api/javax/faces/component/html/HtmlForm.html (the UIComponent behind <h:form>标签)显示它实现了NamingContainer,但是HtmlPanelGroup http://docs.oracle.com/javaee/7/api/javax/faces/component/html/HtmlPanelGroup.html (the UIComponent behind <h:panelGroup>tag)没有显示它,所以它没有实现NamingContainer. 这是所有标准组件的javadoc http://docs.oracle.com/javaee/7/api/javax/faces/component/html/package-summary.html and 这是 PrimeFaces 的 javadoc http://www.primefaces.org/docs/api/.

解决您的问题

所以在你的情况下:

<p:tabView id="tabs"><!-- This is a NamingContainer -->
    <p:tab id="search"><!-- This is NOT a NamingContainer -->
        <h:form id="insTable"><!-- This is a NamingContainer -->
            <p:dialog id="dlg"><!-- This is NOT a NamingContainer -->
                <h:panelGrid id="display">

生成的 HTML 输出<h:panelGrid id="display">看起来像这样:

<table id="tabs:insTable:display">

你需要明白这一点id作为客户端 ID,然后加上前缀:用于update:

<p:commandLink update=":tabs:insTable:display">

引用外部 include/tagfile/composite

如果此命令链接位于 include/tagfile 内部,而目标位于其外部,因此您不一定知道当前命名容器的父级命名容器的 ID,那么您可以通过以下方式动态引用它:UIComponent#getNamingContainer()像这样:

<p:commandLink update=":#{component.namingContainer.parent.namingContainer.clientId}:display">

或者,如果此命令链接位于复合组件内部而目标位于其外部:

<p:commandLink update=":#{cc.parent.namingContainer.clientId}:display">

或者,如果命令链接和目标都位于同一复合组件内:

<p:commandLink update=":#{cc.clientId}:display">

也可以看看获取渲染/更新属性模板中父命名容器的 ID https://stackoverflow.com/questions/8847096/get-id-of-parent-naming-container-in-template-for-in-render-update-attribute/

它是如何在幕后工作的

这一切都被指定为“搜索表达式” in the UIComponent#findComponent() javadoc http://docs.oracle.com/javaee/7/api/javax/faces/component/UIComponent.html#findComponent-java.lang.String-:

A 搜索表达式由一个标识符组成(与一个标识符的 id 属性完全匹配)UIComponent,或由以下链接的一系列此类标识符UINamingContainer#getSeparatorChar字符值。搜索算法应按如下方式运行,但只要最终结果相同,就可以使用替代算法:

  • Identify the UIComponent that will be the base for searching, by stopping as soon as one of the following conditions is met:
    • 如果搜索表达式以分隔符开头(称为“绝对”搜索表达式),则基数将是根UIComponent的组件树。前导分隔符将被去除,搜索表达式的其余部分将被视为“相对”搜索表达式,如下所述。
    • 否则,如果这UIComponent is a NamingContainer它将作为基础。
    • 否则,搜索该组件的父组件。如果一个NamingContainer遇到了,就会成为基地。
    • 否则(如果没有NamingContainer遇到)根UIComponent将是基础。
  • The search expression (possibly modified in the previous step) is now a "relative" search expression that will be used to locate the component (if any) that has an id that matches, within the scope of the base component. The match is performed as follows:
    • 如果搜索表达式是一个简单的标识符,则将该值与 id 属性进行比较,然后递归地遍历基本的方面和子项UIComponent(除非有后代NamingContainer被发现时,不会搜索其自己的方面和子项)。
    • 如果搜索表达式包含多个由分隔符分隔的标识符,则使用第一个标识符来定位NamingContainer按照上一个要点中的规则。然后,findComponent()这个方法NamingContainer将被调用,传递搜索表达式的其余部分。

请注意,PrimeFaces 还遵守 JSF 规范,但 RichFaces 使用“一些额外的例外” http://docs.jboss.org/richfaces/latest_3_3_X/en/devguide/html/ArchitectureOverview.html#Re-Rendering.

“重新渲染” uses UIComponent.findComponent()算法(有一些额外的例外)在组件树中查找组件。

这些额外的例外情况没有详细描述,但众所周知,相对组件 ID(即那些不以:)不仅在最接近的父级上下文中搜索NamingContainer,而且在所有其他NamingContainer同一视图中的组件(顺便说一句,这是一项相对昂贵的工作)。

切勿使用prependId="false"

如果这一切仍然不起作用,请验证您是否没有使用<h:form prependId="false">。这将在处理 ajax 提交和渲染期间失败。另请参阅此相关问题:UIForm 与 prependId="false" 中断 https://stackoverflow.com/questions/7415230/jsf-namingcontainer-and-uiform-with-prependid.

引用迭代组件的特定迭代轮次

很长一段时间以来,不可能在迭代组件中引用特定的迭代项,例如<ui:repeat> and <h:dataTable>像这样:

<h:form id="form">
    <ui:repeat id="list" value="#{['one','two','three']}" var="item">
        <h:outputText id="item" value="#{item}" /><br/>
    </ui:repeat>

    <h:commandButton value="Update second item">
        <f:ajax render=":form:list:1:item" />
    </h:commandButton>
</h:form>

然而,从 Mojarra 2.2.5 开始<f:ajax>开始支持它(它只是停止验证它;因此您将永远不会再遇到问题中提到的异常;稍后计划进行另一个增强修复)。

这仅在当前的 MyFaces 2.2.7 和 PrimeFaces 5.2 版本中不起作用。未来版本可能会提供支持。与此同时,最好的选择是更新迭代组件本身,或者更新父组件,以防它不渲染 HTML,例如<ui:repeat>.

使用 PrimeFaces 时,请考虑搜索表达式或选择器

PrimeFaces 搜索表达式 http://www.primefaces.org/showcase/ui/ajax/search.xhtml允许您通过 JSF 组件树搜索表达式引用组件。 JSF 有几个内置的:

  • @this:当前组件
  • @form: 家长UIForm
  • @all: 整个文档
  • @none: 没有什么

PrimeFaces 通过新的关键字和复合表达式支持增强了这一点:

  • @parent: 父组件
  • @namingcontainer: 家长UINamingContainer
  • @widgetVar(name):由给定标识的组件widgetVar

您还可以在复合表达式中混合这些关键字,例如@form:@parent, @this:@parent:@parent, etc.

PrimeFaces 选择器 (PFS) http://www.primefaces.org/showcase/ui/ajax/selector.xhtml as in @(.someclass)允许您通过 jQuery CSS 选择器语法引用组件。例如。在 HTML 输出中引用具有所有公共样式类的组件。如果您需要引用“很多”组件,这尤其有用。这仅要求目标组件在 HTML 输出中具有所有客户端 ID(固定或自动生成,无关紧要)。也可以看看update="@(.myClass)" 中的 PrimeFaces 选择器如何工作? https://stackoverflow.com/questions/20080861/how-do-primefaces-selectors-as-in-update-myclass-work

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

如何找到ajax更新/渲染组件的客户端ID?找不到带有从“bar”引用的表达式“foo”的组件 的相关文章

随机推荐