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

How to find out client ID of component for ajax update/render? Cannot find component with expression "foo" referenced from "bar"

提问人:perissf 提问时间:12/26/2011 最后编辑:BalusCperissf 更新时间:7/1/2022 访问量:182106

问:

以下代码的灵感来自 PrimeFaces DataGrid + DataTable 教程,并放入驻留在 a of a 中。这是代码的内部部分(从组件开始);外部是微不足道的。<p:tab><p:tabView><p:layoutUnit><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>

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

当我尝试使用相同的方法时,它会失败,并显示一条不同的消息,基本上告诉相同:<f:ajax>

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

当它发生在另一个 Ajax 回发期间,并且 JSF 项目阶段设置为 ,则它会失败,并显示 JavaScript 警报,并显示以下消息Development

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

这是怎么造成的,我该如何解决?

ajax jsf jsf-2 primefaces clientid

评论


答:

9赞 Daniel 12/26/2011 #1

首先:据我所知,将对话框放在 TabView 中是一种不好的做法......你最好把它拿出来......

现在来回答你的问题:

对不起,我花了一些时间才明白你到底想要实现什么,

刚才我自己在我的网络应用程序上做了,它有效

正如我之前所说,将 p:dialog 放在 'p:tabView ,

按照您最初的建议保留 p:dialog:

<p:dialog modal="true" widgetVar="dlg">
    <h:panelGrid id="display">
        <h:outputText value="Name:" />
        <h:outputText value="#{instrumentBean.selectedInstrument.name}" />
    </h:panelGrid>
</p:dialog>   

p:commandlink 应该看起来像这样(我所做的只是更改更新属性)

<p:commandLink update="display" oncomplete="dlg.show()">
    <f:setPropertyActionListener value="#{lndInstrument}" 
        target="#{instrumentBean.selectedInstrument}" />
    <h:outputText value="#{lndInstrument.name}" />
</p:commandLink>  

这同样适用于我的 Web 应用程序,如果它不适合您,那么我想您的 Java Bean 代码中有问题......

评论

0赞 Daniel 12/27/2011
我建议你尝试我在答案中写的其他更改(使用绑定和 faces-config 等...),这假设可以解决您的“信息:找不到 compone...”
0赞 perissf 12/27/2011
我再次尝试实施您的第二个建议,但它仍然不起作用。对话框随即打开,但不包含所选项目的数据。日志显示“在视图中找不到标识符为”j_idt31“的组件”,我无法进行更多调试。
0赞 Mr.J4mes 12/26/2011 #2

尝试更改为 .我相信你不能像那样用表单 ID 作为 id 的前缀。update="insTable:display"update="display"

评论

3赞 J Slick 2/16/2017
很古老但具有误导性的答案。请参阅上面的 BalusC 帖子,清楚地显示了组件 ID 的前缀和封闭表单的 ID:<h:form id=“form”> <p:commandLink update=“:otherform:result”> <!-- OK!--> </h:form> <h:form id=“otherform”> <h:panelGroup id=“result” /> </h:form>
5赞 Lyrion 12/26/2011 #3

这是因为选项卡也是一个命名容器......你的更新应该是 你也可以做的是把你的对话框放在窗体外面,仍然在标签内,然后它就是:update="Search:insTable:display"update="Search:display"

338赞 BalusC 12/27/2011 #4

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

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

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

记住组件并始终为它们提供固定的 IDNamingContainer

如果要通过ajax process/execute/update/render引用的组件位于同一NamingContainer父级中,则只需引用其自己的ID。

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

如果它不在同一个 中,那么您需要使用绝对客户端 ID 引用它。绝对客户端 ID 以分隔符开头,默认情况下为 。NamingContainerNamingContainer:

<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>

例如,命名容器组件包括 、 、 、(因此,所有复合组件)等。通过查看生成的 HTML 输出,您可以轻松识别它们,它们的 ID 将附加到所有子组件生成的客户端 ID 之前。请注意,当它们没有固定的 ID 时,JSF 将使用自动生成的 ID 格式。您绝对应该通过给他们一个固定的 ID 来避免这种情况。在开发过程中,OmniFaces NoAutoGeneratedIdViewHandler 可能会有所帮助。<h:form><h:dataTable><p:tabView><cc:implementation>j_idXXX

如果你知道要找到有问题的 javadoc,那么你也可以在那里检查它是否实现了 NamingContainer 接口。例如,HtmlForm(后面标签)显示它实现了,但 HtmlPanelGroup(后面标签)没有显示它,因此它没有实现。这是所有标准组件的javadoc这是PrimeFaces的javadocUIComponentUIComponent<h:form>NamingContainerUIComponent<h:panelGroup>NamingContainer

解决您的问题

因此,在您的情况下:

<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,然后在以下位置以 for usage 为前缀: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 for in render / update 属性

它是如何在幕后工作的

这一切都在 UIComponent#findComponent() javadoc 中被指定为“搜索表达式”:

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

  • 确定将成为搜索基础的,只要满足以下条件之一,就停止:UIComponent
    • 如果搜索表达式以分隔符开头(称为“绝对”搜索表达式),则基值将是组件树的根。前导分隔符将被剥离,搜索表达式的其余部分将被视为“相对”搜索表达式,如下所述。UIComponent
    • 否则,如果这是一个,它将作为基础。UIComponentNamingContainer
    • 否则,请搜索此组件的父级。如果遇到 a,它将是基础。NamingContainer
    • 否则(如果遇到 no),根将是基数。NamingContainerUIComponent
  • 搜索表达式(可能在上一步中修改)现在是一个“相对”搜索表达式,用于在基本组件的范围内查找 ID 匹配的组件(如果有)。比赛按如下方式进行:
    • 如果搜索表达式是简单标识符,则将此值与 id 属性进行比较,然后递归地遍历基的分面和子项(除非找到后代,则不搜索其自己的分面和子项)。UIComponentNamingContainer
    • 如果搜索表达式包含多个由分隔符分隔的标识符,则第一个标识符用于查找前一个项目符号点中的规则。然后,将调用 this 方法,传递搜索表达式的其余部分。NamingContainerfindComponent()NamingContainer

请注意,PrimeFaces 也遵循 JSF 规范,但 RichFaces 使用“一些额外的例外”。

“reRender”使用算法(有一些额外的例外)在组件树中查找组件。UIComponent.findComponent()

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

从不使用prependId="false"

如果这一切仍然不起作用,请验证您是否未使用 .在处理 ajax 提交和渲染期间,这将失败。另请参阅此相关问题:带有 prependId=“false” 的 UIForm 会中断 <f:ajax render><h:form prependId="false">

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

很长一段时间以来,不可能在迭代组件中引用特定的迭代项,例如:<ui:repeat><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 搜索表达式允许您通过 JSF 组件树搜索表达式来引用组件。JSF 有几个内置的:

  • @this:电流分量
  • @form:父母UIForm
  • @all:整个文档
  • @none:无

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

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

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

PrimeFaces 选择器 (PFS) 允许您通过 jQuery CSS 选择器语法引用组件。例如,引用在 HTML 输出中具有所有通用样式类的组件。这在您需要引用“大量”组件时特别有用。这仅要求目标组件在 HTML 输出中具有所有客户端 ID(固定或自动生成,无关紧要)。参见 update=“@(.myClass)” 中的 PrimeFaces 选择器如何工作?@(.someclass)

评论

0赞 BalusC 1/23/2013
@jack:只需阅读 javadoc:docs.oracle.com/javaee/6/api/javax/faces/component/...从 JSF 2.0 开始,它变成了可配置的,而不是常量。
0赞 jacktrades 1/23/2013
SEPARATOR_CHAR不是被弃用了吗?您能否举例说明如何调用嵌套组件,例如: 另外,请包含 xhtml 代码。context.getViewRoot().findComponent(":inputform" + UINamingContainer.getSeparatorChar(context) + "inputtext" );
1赞 Gaim 7/29/2013
谢谢你,请注意在表单中失败的ajax重新渲染,节省了我的一天。prependId="false"
0赞 Steve Oh 10/11/2013
您的解释中概述的客户 ID 的确切含义是什么?是否与 JSF -> 中的“此组件的客户端标识符”相同。问候+感谢您的工作。
1赞 BalusC 12/4/2015
@antonu17:如答案所述,它仅在 Mojarra 的 f:ajax 中受支持。
1赞 jeff 3/21/2019 #5

我知道BalusC已经给出了一个很好的答案,但这里有一个小技巧,我用它来让容器告诉我正确的clientId

  1. 删除组件上无法正常工作的更新
  2. 将具有虚假更新的临时组件放入您尝试更新的组件中
  3. 点击页面,servlet 异常错误会告诉你需要引用的正确客户端 ID。
  4. 删除虚假组件并在原始更新中放置正确的 clientId

这是代码示例,因为我的话可能无法最好地描述它。

<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"

删除此组件中失败的更新

 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">

在尝试使用将失败的更新进行更新的 ID 的组件中添加一个组件

   <p:commandButton id="BogusButton" update="BogusUpdate"></p:commandButton>

                    <h:outputText value="Name:" />
                    <h:outputText value="#{instrumentBean.selectedInstrument.name}" />
                </h:panelGrid>
            </p:dialog>                            
        </h:form>
    </p:tab>
</p:tabView>

点击此页面并查看错误。 错误是: javax.servlet.ServletException:找不到从 tabs:insTable 引用的表达式“BogusUpdate”的组件:BogusButton: BogusButton

因此,要使用的正确 clientId 将是粗体加上目标容器的 id(在本例中显示)

tabs:insTable:display
4赞 Jasper de Vries 7/1/2022 #6

请注意,从 PrimeFaces 10 及更高版本开始,您可以使用观察者和事件。

这允许您根据关键字设置的自定义事件名称更新组件。例如:@obs(event)

<p:commandButton update="@obs(myEvent)"/>

<h:panelGroup>
    <p:autoUpdate on="myEvent"/>
</h:panelGroup>

看: