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

以下代码的灵感来自 PrimeFaces DataGrid + DataTable 教程,并将其放入位于 <p:layout><p:layoutUnit>中的 <p:tabView><p:tab>中。下面是代码的内部部分(从 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>时,代码停止工作并给出 信息:

无法找到从“ tab: insTable: select”引用表达式“ insTable: display”的组件。

当我使用 <f:ajax>尝试同样的方法时,它失败了,而不同的 信息基本上告诉我相同的信息:

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

当它发生在另一个 Ajax 回发期间,并且 JSF 项目阶段被设置为 Development时,它就会失败,并出现一个带有 信息的 JavaScript 警报:

MalformedXML: 在 update: insTable 期间: display not found

这是怎么引起的,我该怎么解决?

176754 次浏览

首先: 据我所知,把对话框放在桌面视图里是一种不好的做法... ... 你最好把它拿出来... ..。

现在回到你的问题:

抱歉,我花了点时间才弄到你想要的东西,

我刚刚在我的网络应用程序上做了一个,而且很有效

如前所述,将 p: 对话框放在‘ p: tabView 外面,

保留最初建议的 p: 对话框:

<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 应该是这样的(我所做的只是更改了 update 属性)

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

同样的工作在我的网络应用程序,如果它不为你工作,那么我想有一些错误在你的 java bean 代码..。

试着把 update="insTable:display"改成 update="display"。我相信你不能像这样在表单 ID 前面加上前缀。

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

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

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

注意: 如果它碰巧包含像 :0::1:等迭代索引(因为它位于迭代组件内部) ,那么您需要意识到并不总是支持更新特定的迭代轮。更多细节见底部的回答。

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

如果您希望通过 ajax 进程/执行/更新/呈现引用的组件位于相同的 NamingContainer父级中,那么只需引用它自己的 ID 即可。

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

如果它是同一 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 组件是 <h:form><h:dataTable><p:tabView><cc:implementation>(因此,所有复合组件)等。您可以通过查看生成的 HTML 输出轻松地识别它们,它们的 ID 将被预先添加到所有子组件的生成的客户端 ID 中。注意,当它们没有固定的 ID 时,JSF 将使用 j_idXXX格式的自动生成的 ID。你应该给他们一个固定的 ID 来避免这种情况。OmniFaces NoAutoGeneratedIdViewHandler在开发过程中可能会有所帮助。

如果您知道要找到所讨论的 UIComponent的 javadoc,那么您还可以检查它是否实现了 NamingContainer0接口。例如,NamingContainer1(<h:form>标签后面的 UIComponent)显示它实现了 NamingContainer,但是 NamingContainer2(<h:panelGroup>标签后面的 UIComponent)没有显示它,所以它没有实现 NamingContainerNamingContainer3和 NamingContainer4。

解决你的问题

因此,在你的情况下:

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

<h:panelGrid id="display">生成的 HTML 输出如下:

<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

在被子下面是怎么工作的

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

搜索表达式由标识符(与 UIComponent的 id 属性完全匹配)或由 UINamingContainer#getSeparatorChar字符值链接的一系列这样的标识符组成。搜索算法的运作方式如下,但只要最终结果相同,可以使用其他算法:

  • 确定 UIComponent将是搜索的基础,一旦满足下列条件之一即停止:
    • 如果搜索表达式以分隔符字符(称为“绝对”搜索表达式)开始,则基底将是组件树的根 UIComponent。前导分隔符将被去掉,搜索表达式的其余部分将被视为“相对”搜索表达式,如下所述。
    • 否则,如果这个 UIComponent是一个 NamingContainer,它将作为基础。
    • 否则,搜索此组件的父级。如果遇到 NamingContainer,它将是基。
    • 否则(如果没有遇到 NamingContainer)根 UIComponent将是基。
  • 搜索表达式(可能在前面的步骤中修改过)现在是一个“相对”搜索表达式,用于在基本组件的范围内定位具有匹配 id 的组件(如果有的话)。比赛进行如下:
    • 如果搜索表达式是一个简单的标识符,则将该值与 id 属性进行比较,然后递归地通过基 UIComponent的方面和子级(除非找到后代 NamingContainer,否则不搜索其自身的方面和子级)。
    • 如果搜索表达式包含由分隔符分隔的多个标识符,则使用第一个标识符根据前一个项目符号中的规则定位 NamingContainer。然后,将调用这个 NamingContainerfindComponent()方法,传递搜索表达式的其余部分。

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

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

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

永远不要使用 prependId="false"

如果这些仍然不起作用,那么验证您是否没有使用 <h:form prependId="false">。这将在处理 ajax 提交和渲染过程中失败。也请看这个相关的问题: 带有 prepenId = “ false”的 UIForm 中断 < f: ajax 呈现 >

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

在像 <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 Search Expressions 允许您通过 JSF 组件树搜索表达式引用组件:

  • 电流分量
  • 家长 UIForm
  • 整个文件
  • 没什么

PrimeFaces 增加了新的关键字和复合表达式支持:

  • @parent: 父组件
  • 家长 UINamingContainer
  • @widgetVar(name): 由给定的 widgetVar确定的组分

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

PrimeFaces Selectors (PFS) @(.someclass)一样,允许您通过 jQuery CSS 选择器语法引用组件。例如,在 HTML 输出中引用具有所有通用样式类的组件。如果您需要引用“很多”组件,这尤其有帮助。这只需要目标组件在 HTML 输出中具有所有客户端 ID (固定或自动生成,无关紧要)。参见《 PrimeFaces 选择器是如何在 update 中选择的》(http://stackoverflow. com/questions/20080861/How-do-PrimeFaces-selector-as-in-update-myclass-work)。工作?

我知道这已经有一个很好的答案 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>

点击这个页面并查看错误。 错误是: 引用的表达式“ BogusUpdate”的组件 标签: insTable: BogusButton

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

tabs:insTable:display

请注意,从 PrimeFaces 10及以上,您可以使用观察者和事件。

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

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


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

参见: