如何找到ajax update / render组件的客户端ID?找不到带有“bar”引用的表达式“foo”的组件

问题描述 投票:125回答:5

以下代码的灵感来自PrimeFaces DataGrid + DataTable教程,并放入<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>时,代码停止工作并给出消息:

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

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

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

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

ajax jsf jsf-2 primefaces clientid
5个回答
286
投票

Look in HTML output for actual client ID

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

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

Memorize NamingContainer components and always give them a fixed ID

如果您想通过ajax process / execute / update / render引用的组件位于同一个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,那么您也可以检查它是否实现了NamingContainer接口。例如,HtmlFormUIComponent标签背后的<h:form>)显示它实现了NamingContainer,但是HtmlPanelGroupUIComponent标签背后的<h:panelGroup>)没有显示它,所以它没有实现NamingContainerHere is the javadoc of all standard componentshere is the javadoc of PrimeFaces

Solving your problem

所以在你的情况下:

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

Referencing outside include/tagfile/composite

如果此命令链接在include / tagfile中,并且目标在其外部,因此您不一定知道当前命名容器的命名容器父ID的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">

另见Get id of parent naming container in template for in render / update attribute

How does it work under the covers

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

搜索表达式由一个标识符(与UIComponent的id属性完全匹配,或者由UINamingContainer#getSeparatorChar字符值链接的一系列此类标识符)组成。搜索算法应如下操作,尽管可以使用替代算法最终结果是一样的:

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

请注意,PrimeFaces也遵守JSF规范,但RichFaces使用"some additional exceptions"

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

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

Never use prependId="false"

如果这仍然无效,请验证您是否使用<h:form prependId="false">。在处理ajax提交和呈现期间,这将失败。另见相关问题:UIForm with prependId="false" breaks <f:ajax render>

Referencing specific iteration round of iterating components

很长一段时间不可能在像<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>

When using PrimeFaces, consider Search Expressions or Selectors

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

  • @this:当前组成部分
  • @form:父母UIForm
  • @all:整个文件
  • @none:什么都没有

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

  • @parent:父组件
  • @namingcontainer:父母UINamingContainer
  • @widgetVar(name):由给定的widgetVar确定的成分

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

PrimeFaces Selectors (PFS)中的@(.someclass)允许您通过jQuery CSS选择器语法引用组件。例如。引用HTML输出中具有所有通用样式类的组件。如果您需要引用“很多”组件,这将特别有用。这仅预先确定目标组件在HTML输出中具有所有客户端ID(固定或自动生成,无关紧要)。另见How do PrimeFaces Selectors as in update="@(.myClass)" work?


9
投票

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

现在问你的问题:

对不起,花了我一些时间来确定你想要实现什么,

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

正如我之前所说的那样将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代码有问题...


5
投票

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


0
投票

尝试将update="insTable:display"更改为update="display"。我相信你不能像这样给表单ID添加id。


0
投票

我知道这已经得到了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中引用的表达式“BogusUpdate”的组件:insTable:BogusButton

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

tabs:insTable:display
© www.soinside.com 2019 - 2024. All rights reserved.