简体   繁体   English

如何找出用于 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"

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> .以下代码受 PrimeFaces DataGrid + DataTable 教程的启发,并放入<p:tabView><p:tab>中,该 <p:layoutUnit> 位于 <p <p:layout> > 的<p:layoutUnit>中。 Here is the inner part of the code (starting from p:tab component);这是代码的内部部分(从p:tab组件开始); the outer part is trivial.外部是微不足道的。

<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> , then 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" 的上下文中找到它

When it happens during another Ajax postback and the JSF project stage is set to Development , then it fails with a JavaScript alert with the message :当它在另一个 Ajax 回发期间发生并且 JSF 项目阶段设置为Development时,它会失败并显示带有以下消息的 JavaScript 警报:

malformedXML: During update: insTable:display not found malformedXML:更新期间:insTable:找不到显示

How is this caused and how can I solve it?这是如何引起的,我该如何解决?

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.您需要查看生成的 HTML 输出以找出正确的客户端 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 id as client ID.找到感兴趣的 JSF 组件的 HTML 表示并将其id作为客户端 ID。 You can use it in an absolute or relative way depending on the current naming container.您可以根据当前命名容器以绝对或相对方式使用它。 See following chapter.见下一章。

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.注意:如果它恰好包含像:0::1:等迭代索引(因为它在迭代组件内),那么您需要意识到并不总是支持更新特定的迭代轮次。 See bottom of answer for more detail on that.有关详细信息,请参阅答案底部。

Memorize NamingContainer components 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 NamingContainer parent, then just reference its own ID.如果您希望通过 ajax process/execute/update/render 引用的组件位于同一个NamingContainer父级中,则只需引用其自己的 ID。

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

If it's not inside the same NamingContainer , then you need to reference it using an absolute client ID.如果它不在同一个NamingContainer中,那么您需要使用绝对客户端 ID 来引用它。 An absolute client ID starts with the NamingContainer separator character, which is by default : .绝对客户端 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 components 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. NamingContainer组件例如是<h:form><h:dataTable><p:tabView><cc:implementation> (因此,所有复合组件)等。您可以通过查看生成的 HTML 输出轻松识别它们,它们的 ID 将添加到所有子组件的生成客户端 ID 之前。 Note that when they don't have a fixed ID, then JSF will use an autogenerated ID in j_idXXX format.请注意,当它们没有固定 ID 时,JSF 将使用自动生成的j_idXXX格式的 ID。 You should absolutely avoid that by giving them a fixed ID.您应该通过给他们一个固定的 ID 来绝对避免这种情况。 The OmniFaces NoAutoGeneratedIdViewHandler may be helpful in this during development. OmniFaces NoAutoGeneratedIdViewHandler在开发过程中可能会对此有所帮助。

If you know to find the javadoc of the UIComponent in question, then you can also just check in there whether it implements the NamingContainer interface or not.如果您知道要找到有问题的UIComponent的 javadoc,那么您也可以在那里检查它是否实现了NamingContainer接口。 For example, the HtmlForm (the UIComponent behind <h:form> tag) shows it implements NamingContainer , but the HtmlPanelGroup (the UIComponent behind <h:panelGroup> tag) does not show it, so it does not implement NamingContainer .例如, HtmlForm<h:form>标签后面的UIComponent )显示它实现了NamingContainer ,但HtmlPanelGroup<h:panelGroup>标签后面的UIComponent )没有显示它,因此它没有实现NamingContainer Here is the javadoc of all standard components and here is the javadoc of PrimeFaces . 这是所有标准组件的 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: <h:panelGrid id="display">生成的 HTML 输出如下所示:

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

You need to take exactly that id as client ID and then prefix with : for usage in update :您需要将该id准确地用作客户端 ID,然后以:为前缀,以便在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里面,而target在它外面,那么你不一定知道当前命名容器的父命名容器的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 expression consists 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#getSeparatorChar character value. The search algorithm should operates as follows, though alternate alogrithms may be used as long as the end result is the same:搜索表达式包含一个标识符(与UIComponent的 id 属性完全匹配,或者由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
    • If the search expression begins with the the separator character (called an "absolute" search expression), the base will be the root UIComponent of the component tree.如果搜索表达式以分隔符开头(称为“绝对”搜索表达式),则基将是组件树的根UIComponent 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 UIComponent is a NamingContainer it will serve as the basis.否则,如果此UIComponentNamingContainer ,它将作为基础。
    • Otherwise, search up the parents of this component.否则,搜索该组件的父级。 If a NamingContainer is encountered, it will be the base.如果遇到NamingContainer ,它将是基础。
    • Otherwise (if no NamingContainer is encountered) the root UIComponent will be the base.否则(如果没有遇到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.搜索表达式(可能在上一步中修改)现在是一个“相对”搜索表达式,用于在基本组件的范围内定位具有匹配 id 的组件(如果有)。 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 NamingContainer is found, its own facets and children are not searched).如果搜索表达式是一个简单的标识符,则将该值与 id 属性进行比较,然后递归地通过基UIComponent的 facet 和子项(除非找到后代NamingContainer ,则不会搜索其自身的 facet 和子项)。
    • If the search expression includes more than one identifier separated by the separator character, the first identifier is used to locate a NamingContainer by the rules in the previous bullet point.如果搜索表达式包含多个由分隔符分隔的标识符,则第一个标识符用于根据上一个项目符号中的规则定位NamingContainer Then, the findComponent() method of this NamingContainer will be called, passing the remainder of the search expression.然后,将调用此NamingContainerfindComponent()方法,传递搜索表达式的其余部分。

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 (ie those not starting with : ) are not only searched in the context of the closest parent NamingContainer , but also in all other NamingContainer components 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"> .如果这一切仍然不起作用,请验证您是否没有使用<h:form prependId="false"> This will fail during processing the ajax submit and render.这将在处理 ajax 提交和渲染期间失败。 See also this related question: UIForm with prependId="false" breaks <f:ajax render> .另请参阅此相关问题: UIForm with prependId="false" break <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.这只在当前的 MyFaces 2.2.7 和 PrimeFaces 5.2 版本中还不起作用。 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> .同时,最好的办法是更新迭代组件本身,或者更新父组件以防它不呈现 HTML,例如<ui:repeat>

When using PrimeFaces, consider Search Expressions or Selectors使用 PrimeFaces 时,请考虑搜索表达式或选择器

PrimeFaces Search Expressions allows you to reference components via JSF component tree search expressions. PrimeFaces Search Expressions允许您通过 JSF 组件树搜索表达式引用组件。 JSF has several builtin: JSF 有几个内置的:

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

PrimeFaces has enhanced this with new keywords and composite expression support: PrimeFaces 通过新的关键字和复合表达式支持增强了这一点:

  • @parent : parent component @parent : 父组件
  • @namingcontainer : parent UINamingContainer @namingcontainer : 父UINamingContainer
  • @widgetVar(name) : component as identified by given widgetVar @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. @(.someclass)中的PrimeFaces 选择器 (PFS)允许您通过 jQuery CSS 选择器语法引用组件。 Eg referencing components having all a common style class in the HTML output.例如,在 HTML 输出中引用具有所有通用样式类的组件。 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).这仅要求目标组件在 HTML 输出中具有所有客户端 ID(固定或自动生成,无关紧要)。 See also How do PrimeFaces Selectors as in update="@(.myClass)" work?另请参阅update="@(.myClass)" 中的 PrimeFaces 选择器如何工作?

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 代码有问题......

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

Please note that from PrimeFaces 10 and up, you are able to use observer and event.请注意,从 PrimeFaces 10 起,您可以使用观察者和事件。

This allows you to update components based on a custom event name, set by the @obs(event) keyword.这允许您根据由@obs(event)关键字设置的自定义事件名称来更新组件。 For example:例如:

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

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

See:看:

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.点击页面,servlet 异常错误会告诉您需要引用的正确客户端 ID。
  4. Remove bogus component and put correct clientId in the original update删除虚假组件并将正确的 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:找不到从选项卡引用的表达式“BogusUpdate”的组件:insTable: BogusButton

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

Try change update="insTable:display" to update="display" .尝试将update="insTable:display"更改为update="display" I believe you cannot prefix the id with the form ID like that.我相信你不能像这样在表单 ID 前面加上 id。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM