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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/8634156/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-06 11:05:54  来源:igfitidea点击:

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

ajaxjsfjsf-2primefacesclientid

提问by perissf

The following code is inspired from PrimeFaces DataGrid + DataTable Tutorials and put into a <p:tab>of a <p:tabView>residing in a <p:layoutUnit>of a <p:layout>. Here is the inner part of the code (starting from p:tabcomponent); the outer part is trivial.

以下代码受 PrimeFaces DataGrid + DataTable Tutorials 启发,放入 a <p:tab>of a 中,<p:tabView>位于 a <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>

When I click the <p:commandLink>, the code stops working and gives the message:

当我单击 时<p:commandLink>,代码停止工作并给出消息:

Cannot find component with expression "insTable:display" referenced from "tabs:insTable:select".

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

When I try the same using <f:ajax>, it fails with a different message basically telling the same:

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

<f:ajax>contains an unknown id "insTable:display" cannot locate it in the context of the component "tabs:insTable:select"

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

How is this caused and how can I solve it?

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

回答by BalusC

Look in HTML output for actual client ID

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

You need to look in the generated HTML output to find out the right client ID. Open the page in browser, do a rightclick and View Source. Locate the HTML representation of the JSF component of interest and take its idas client ID. You can use it in an absolute or relative way depending on the current naming container. See following chapter.

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

Note: if it happens to contain iteration index like :0:, :1:, etc (because it's inside an iterating component), then you need to realize that updating a specific iteration round is not always supported. See bottom of answer for more detail on that.

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

Memorize NamingContainercomponents and always give them a fixed ID

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

If a component which you'd like to reference by ajax process/execute/update/render is inside the same NamingContainerparent, then just reference its own ID.

如果您希望由 ajax 进程/执行/更新/渲染NamingContainer引用的组件位于同一个父级内,则只需引用其自己的 ID。

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

If it's notinside the same NamingContainer, then you need to reference it using an absolute client ID. An absolute client ID starts with the NamingContainerseparator character, which is by default :.

如果它不在同一个 中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>

NamingContainercomponents are for example <h:form>, <h:dataTable>, <p:tabView>, <cc:implementation>(thus, all composite components), etc. You recognize them easily by looking at the generated HTML output, their ID will be prepended to the generated client ID of all child components. Note that when they don't have a fixed ID, then JSF will use an autogenerated ID in j_idXXXformat. You should absolutely avoid that by giving them a fixed ID. The OmniFaces NoAutoGeneratedIdViewHandlermay be helpful in this during development.

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

If you know to find the javadoc of the UIComponentin question, then you can also just check in there whether it implements the NamingContainerinterface or not. For example, the HtmlForm(the UIComponentbehind <h:form>tag) shows it implements NamingContainer, but the HtmlPanelGroup(the UIComponentbehind <h:panelGroup>tag) does not show it, so it does not implement NamingContainer. Here is the javadoc of all standard componentsand here is the javadoc of PrimeFaces.

如果您知道找到有UIComponent问题的 javadoc ,那么您也可以在那里检查它是否实现了NamingContainer接口。例如,HtmlFormUIComponent后面的<h:form>标签)显示它实现了NamingContainer,但是HtmlPanelGroupUIComponent后面的<h:panelGroup>标签)没有显示它,所以它没有实现NamingContainer这里是所有标准组件的Javadoc,并在这里是PrimeFaces的javadoc的

Solving your problem

解决您的问题

So in your case of:

所以在你的情况下:

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

The generated HTML output of <h:panelGrid id="display">looks like this:

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

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

You need to take exactly that idas client ID and then prefix with :for usage in update:

您需要将其id作为客户端 ID,然后使用:for前缀update

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

Referencing outside include/tagfile/composite

引用外部 include/tagfile/composite

If this command link is inside an include/tagfile, and the target is outside it, and thus you don't necessarily know the ID of the naming container parent of the current naming container, then you can dynamically reference it via UIComponent#getNamingContainer()like so:

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

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

Or, if this command link is inside a composite component and the target is outside it:

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

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

Or, if both the command link and target are inside same composite component:

或者,如果命令链接和目标都在同一个复合组件中:

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

See also Get id of parent naming container in template for in render / update attribute

另请参阅在模板中获取父命名容器的 id 以获取渲染/更新属性

How does it work under the covers

它是如何在幕后工作的

This all is specified as "search expression"in the UIComponent#findComponent()javadoc:

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

A search expressionconsists of either an identifier (which is matched exactly against the id property of a UIComponent, or a series of such identifiers linked by the UINamingContainer#getSeparatorCharcharacter value. The search algorithm should operates as follows, though alternate alogrithms may be used as long as the end result is the same:

  • Identify the UIComponentthat will be the base for searching, by stopping as soon as one of the following conditions is met:
    • If the search expression begins with the the separator character (called an "absolute" search expression), the base will be the root UIComponentof the component tree. The leading separator character will be stripped off, and the remainder of the search expression will be treated as a "relative" search expression as described below.
    • Otherwise, if this UIComponentis a NamingContainerit will serve as the basis.
    • Otherwise, search up the parents of this component. If a NamingContaineris encountered, it will be the base.
    • Otherwise (if no NamingContaineris encountered) the root UIComponentwill be the base.
  • 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:
    • If the search expression is a simple identifier, this value is compared to the id property, and then recursively through the facets and children of the base UIComponent(except that if a descendant NamingContaineris found, its own facets and children are not searched).
    • If the search expression includes more than one identifier separated by the separator character, the first identifier is used to locate a NamingContainerby the rules in the previous bullet point. Then, the findComponent()method of this NamingContainerwill be called, passing the remainder of the search expression.

搜索表达式由任一的标识符(其精确匹配对的id属性UIComponent,或一系列由链接这样的标识符的UINamingContainer#getSeparatorChar字符值。该搜索算法应该如下操作,尽管替代算法的可以使用,只要该最终结果是一样的:

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

Note that PrimeFaces also adheres the JSF spec, but RichFaces uses "some additional exceptions".

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

"reRender"uses UIComponent.findComponent()algorithm (with some additional exceptions) to find the component in the component tree.

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

Those additional exceptions are nowhere in detail described, but it's known that relative component IDs (i.e. those not starting with :) are not only searched in the context of the closest parent NamingContainer, but also in all other NamingContainercomponents in the same view (which is a relatively expensive job by the way).

这些额外的例外是无处中详细描述的,但它的已知的是,相对的组件ID(即那些不与起始:)不仅在最近的父的上下文中搜索NamingContainer,而且在所有其它NamingContainer在相同的视图分量(其是相对顺便说一句,昂贵的工作)。

Never use prependId="false"

从不使用 prependId="false"

If this all still doesn't work, then verify if you aren't using <h:form prependId="false">. This will fail during processing the ajax submit and render. See also this related question: UIForm with prependId="false" breaks <f:ajax render>.

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

Referencing specific iteration round of iterating components

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

It was for long time not possible to reference a specific iterated item in iterating components like <ui:repeat>and <h:dataTable>like so:

这是长时间无法引用特定的迭代类似的组件重复项目<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>

However, since Mojarra 2.2.5 the <f:ajax>started to support it (it simply stopped validating it; thus you would never face the in the question mentioned exception anymore; another enhancement fix is planned for that later).

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

This only doesn't work yet in current MyFaces 2.2.7 and PrimeFaces 5.2 versions. The support might come in the future versions. In the meanwhile, your best bet is to update the iterating component itself, or a parent in case it doesn't render HTML, like <ui:repeat>.

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

When using PrimeFaces, consider Search Expressions or Selectors

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

PrimeFaces Search Expressionsallows you to reference components via JSF component tree search expressions. JSF has several builtin:

PrimeFaces Search Expressions允许您通过 JSF 组件树搜索表达式来引用组件。JSF 有几个内置:

  • @this: current component
  • @form: parent UIForm
  • @all: entire document
  • @none: nothing
  • @this: 当前组件
  • @form: 家长 UIForm
  • @all: 整个文件
  • @none: 没有

PrimeFaces has enhanced this with new keywords and composite expression support:

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

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

You can also mix those keywords in composite expressions such as @form:@parent, @this:@parent:@parent, etc.

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

PrimeFaces Selectors (PFS)as in @(.someclass)allows you to reference components via jQuery CSS selector syntax. E.g. referencing components having all a common style class in the HTML output. This is particularly helpful in case you need to reference "a lot of" components. This only prerequires that the target components have all a client ID in the HTML output (fixed or autogenerated, doesn't matter). See also How do PrimeFaces Selectors as in update="@(.myClass)" work?

PrimeFaces Selectors (PFS)as in@(.someclass)允许您通过 jQuery CSS 选择器语法引用组件。例如,引用在 HTML 输出中具有所有通用样式类的组件。这在您需要引用“很多”组件的情况下特别有用。这只是预先要求目标组件在 HTML 输出中具有所有客户端 ID(固定或自动生成,无关紧要)。另请参阅update="@(.myClass)" 中的 PrimeFaces 选择器如何工作?

回答by Daniel

first of all: as far as i know placing dialog inside a tabview is a bad practice... you better take it out...

首先:据我所知,在 tabview 中放置对话框是一种不好的做法……你最好把它去掉……

and now to your question:

现在回答你的问题:

sorry, took me some time to get what exactly you wanted to implement,

抱歉,我花了一些时间才弄明白您想要实现的内容,

did at my web app myself just now, and it works

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

as I sayed before place the p:dialog out side the `p:tabView ,

正如我之前所说,将 p:dialog 放在 `p:tabView 旁边,

leave the p:dialog as you initially suggested :

按照您最初的建议离开 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>   

and the p:commandlink should look like this (all i did is to change the update attribute)

和 p:commandlink 应该是这样的(我所做的就是改变更新属性)

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

the same works in my web app, and if it does not work for you , then i guess there is something wrong in your java bean code...

在我的网络应用程序中同样有效,如果它对你不起作用,那么我猜你的 java bean 代码有问题......

回答by Lyrion

It's because the tab is a naming container aswell... your update should be update="Search:insTable:display"What you can do aswell is just place your dialog outside the form and still inside the tab then it would be: update="Search:display"

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

回答by Mr.J4mes

Try change update="insTable:display"to update="display". I believe you cannot prefix the id with the form ID like that.

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

回答by jeff

I know this already has a great answer by BalusC but here is a little trick I use to get the container to tell me the correct clientId.

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

  1. Remove the update on your component that is not working
  2. Put a temporary component with a bogus update within the component you were trying to update
  3. hit the page, the servlet exception error will tell you the correct client Id you need to reference.
  4. Remove bogus component and put correct clientId in the original update
  1. 删除不工作的组件上的更新
  2. 在您尝试更新的组件中放置一个带有虚假更新的临时组件
  3. 点击页面,servlet 异常错误会告诉您需要引用的正确客户端 ID。
  4. 删除虚假组件并在原始更新中放置正确的 clientId

Here is code example as my words may not describe it best.

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

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

Remove the failing update within this component

删除此组件中的失败更新

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

Add a component within the component of the id you are trying to update using an update that will fail

在您尝试使用将失败的更新更新的 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>

Hit this page and view the error. The error is: javax.servlet.ServletException: Cannot find component for expression "BogusUpdate" referenced from tabs:insTable:BogusButton

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

So the correct clientId to use would then be the bold plus the id of the target container (display in this case)

因此,要使用的正确 clientId 将是粗体加上目标容器的 id(在这种情况下显示)

tabs:insTable:display