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


140

下面的代码是从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>,代码停止工作并显示以下消息:

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

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

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

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

Answers:


313

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

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

注意:如果它恰好包含诸如:0::1:等的迭代索引(因为它位于迭代组件中),那么您需要意识到并不总是支持更新特定的迭代回合。有关详细信息,请参见答案底部。

记住NamingContainer组件并始终为其提供固定的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。该OmniFacesNoAutoGeneratedIdViewHandler发展过程中可这很有帮助。

如果您知道要查找相关的javadoc UIComponent,那么也可以仅在其中检查它是否实现了NamingContainer接口。例如,HtmlForm(标记UIComponent后面<h:form>)显示了它的实现NamingContainer,但是HtmlPanelGroup(标记UIComponent后面<h:panelGroup>)没有显示,因此它没有实现NamingContainer这里是所有标准组件的Javadoc,并在这里是PrimeFaces的javadoc的

解决你的问题

因此,在您的情况下:

<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用作客户端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">

另请参阅在render / update属性中获取模板中父命名容器的ID

它是如何工作的

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

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

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

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

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

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

永远不要使用 prependId="false"

如果这一切仍然不起作用,请确认您是否未使用<h:form prependId="false">。这将在处理ajax提交和呈现期间失败。另请参见以下相关问题:具有prependId =“ false”的UIForm破坏了<f:ajax render>

引用迭代组件的特定迭代回合

这是长时间无法引用特定的迭代类似的组件重复项目<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 Selectors(PFS)@(.someclass)允许您通过jQuery CSS选择器语法引用组件。例如,引用具有HTML输出中所有通用样式类的组件。如果您需要引用“很多”组件,这特别有用。这仅需要目标组件在HTML输出中具有所有客户端ID(固定或自动生成,都没有关系)。另请参见update =“ @(。myClass)”中的PrimeFaces Selectors如何工作?


@jack:只需阅读javadoc:docs.oracle.com/javaee/6/api/javax/faces/component / ...从JSF 2.0开始,它已成为可配置的,而不是常量。
BalusC 2013年

SEPARATOR_CHAR是否已弃用?您能否举一个有关如何调用嵌套组件的示例,例如:context.getViewRoot().findComponent(":inputform" + UINamingContainer.getSeparatorChar(context) + "inputtext" );还请包括xhtml代码。
jacktrades

1
谢谢,请注意在prependId="false"保存Ajax表单时失败并保存了我的一天。
盖姆

您的解释中概述的客户ID的确切含义是什么?是否与JSF->“此组件的客户端标识符”中的相同?谢谢您的工作。
史蒂夫·噢

1
@ antonu17:如答案中所述,仅Mojarra的f:ajax支持它。
BalusC 2015年

9

首先:据我所知,在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应该看起来像这样(我所做的就是更改update属性)

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

在我的Web应用程序中同样有效,如果对您不起作用,那么我想您的Java bean代码中有问题...


我建议您尝试我在答案中写的其他更改(使用binding和faces-config等...),以解决您的“ INFO:Cannot find compone ...”
Daniel

我再次尝试实施您的第二个建议,但仍然无法正常工作。对话框打开,但不包含所选项目的数据。日志显示“在视图中找不到标识符为“ j_idt31”的组件”,除此以外,我无法进行其他调试。
perissf 2011年

5

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


0

尝试更改update="insTable:display"update="display"。我相信您不能在ID前面加上这样的表单ID。


2
很老但误导性的答案。请参阅上面的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>
J Slick

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:insTable: BogusButton 引用的表达式“ BogusUpdate”的组件

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

tabs:insTable:display
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.