151

以下代码灵感来自 PrimeFaces DataGrid + DataTable Tutorials,并放入 a of <p:tab>a中。这是代码的内部部分(从组件开始);外部是微不足道的。<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”的组件。

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

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

当它在另一个 Ajax 回发期间发生并且 JSF 项目阶段设置为 时Development,它会失败并显示带有以下消息的 JavaScript 警报:

malformedXML:更新期间:insTable:找不到显示

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

4

5 回答 5

331

在 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可能会对此有所帮助。

如果您知道找到有UIComponent问题的 javadoc,那么您也可以在那里检查它是否实现了NamingContainer接口。例如,HtmlFormUIComponent后面的<h:form>标签)显示它实现NamingContainer了,但HtmlPanelGroupUIComponent后面的<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

如果此命令链接在包含/标记文件内,而目标在其中,因此您不一定知道当前命名容器的命名容器父级的 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

它是如何在幕后工作的

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

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

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

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

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

这些额外的异常没有详细描述,但众所周知,相对组件 ID(即:那些NamingContainerNamingContainer以顺便说一句昂贵的工作)。

从不使用prependId="false"

如果这一切仍然不起作用,请验证您是否没有使用<h:form prependId="false">. 这将在处理 ajax 提交和渲染期间失败。另请参阅此相关问题:UIForm with prependId="false" break <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 Search Expressions允许您通过 JSF 组件树搜索表达式引用组件。JSF 有几个内置的:

  • @this: 当前组件
  • @form: 父母UIForm
  • @all: 整个文档
  • @none: 没有

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

  • @parent: 父组件
  • @namingcontainer: 父母UINamingContainer
  • @widgetVar(name): 由给定标识的组件widgetVar

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

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

于 2011-12-27T12:42:53.870 回答
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 应该是这样的(我所做的只是更改更新属性)

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

这同样适用于我的网络应用程序,如果它不适合你,那么我猜你的 java bean 代码有问题......

于 2011-12-26T09:11:19.593 回答
5

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

于 2011-12-26T10:59:36.327 回答
1

我知道 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:找不到从选项卡引用的表达式“BogusUpdate”的组件 :insTable: BogusButton

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

tabs:insTable:display
于 2019-03-21T14:20:27.260 回答
0

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

于 2011-12-26T09:25:56.717 回答