有 Java 编程相关的问题?

你可以在下面搜索框中键入要查询的问题!

jsf如何找到ajax更新/呈现组件的客户端ID?找不到从“bar”引用的表达式为“foo”的组件

下面的代码受PrimeFaces DataGrid+DataTable教程的启发,并放入位于<p:layout><p:layoutUnit>中的<p:tab><p:tabView>中。下面是代码的内部部分(从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>时,代码停止工作,并给出message

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

当我使用<f:ajax>尝试相同的方法时,它失败了,不同的message基本上告诉了我相同的信息:

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

当它发生在另一个Ajax回发期间,并且JSF项目阶段设置为Development,那么它将失败,并发出带有message的JavaScript警报:

malformedXML: During update: insTable:display not found

这是如何造成的,我如何解决


共 (5) 个答案

  1. # 1 楼答案

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

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

    注意:如果它恰好包含:0::1:等迭代索引(因为它位于迭代组件内部),那么您需要意识到并不总是支持更新特定的迭代轮。关于这方面的更多细节,请参见答案的底部

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

    如果您希望通过ajax进程/execute/update/render引用的组件位于同一个^{}父级中,那么只需引用它自己的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>
    

    ^{}组件是例如<h:form><h:dataTable><p:tabView><cc:implementation>(因此,所有复合组件)等。通过查看生成的HTML输出,您可以很容易地识别它们,它们的ID将放在所有子组件的生成的客户端ID之前。注意,当它们没有固定ID时,JSF将使用自动生成的j_idXXX格式的ID。您应该通过给他们一个固定的ID来避免这种情况。在开发过程中,OmniFaces ^{}可能会在这方面有所帮助

    如果您知道如何找到所讨论的UIComponent的javadoc,那么您也可以在那里检查它是否实现了^{}接口。例如,^{}(在<h:form>后面的UIComponent标记)显示它实现了NamingContainer,但是^{}(在UIComponent后面的<h:panelGroup>标记)没有显示它,因此它没有实现NamingContainerHere is the javadoc of all standard componentshere is the javadoc of PrimeFaces

    解决您的问题

    因此,在您的案例中:

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

    引用外部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">
    

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

    它是如何在封面下工作的

    这一切都在the ^{} 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:

    • Identify the UIComponent that 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 UIComponent of 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 UIComponent is a NamingContainer it will serve as the basis.
      • Otherwise, search up the parents of this component. If a NamingContainer is encountered, it will be the base.
      • Otherwise (if no NamingContainer is encountered) the root UIComponent will 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 NamingContainer is 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 NamingContainer by the rules in the previous bullet point. Then, the findComponent() method of this NamingContainer will be called, passing the remainder of the search expression.

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

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

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

    切勿使用prependId="false"

    如果这一切仍然不起作用,那么请验证您是否没有使用<h:form prependId="false">。这将在处理ajax提交和渲染过程中失败。另见这一相关问题:UIForm with prependId="false" breaks <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>

    使用PrimeFras时,考虑搜索表达式或选择器< EH3>

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

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

    PrimeFaces通过新的关键字和复合表达式支持对此进行了增强:

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

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

    @(.someclass)中的PrimeFaces Selectors (PFS)允许您通过jQuery CSS选择器语法引用组件。例如,在HTML输出中引用具有所有公共样式类的组件。当您需要引用“很多”组件时,这尤其有用。这只要求目标组件在HTML输出中具有所有客户端ID(固定或自动生成,无所谓)。另见How do PrimeFaces Selectors as in update="@(.myClass)" work?

  2. # 2 楼答案

    首先:据我所知,在选项卡视图中放置对话框是一种不好的做法。。。你最好把它拿出来

    现在谈谈你的问题:

    对不起,我花了一些时间才得到你想要实现的东西

    刚才我自己在我的网络应用上做了,而且很有效

    正如我之前所说的,将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>  
    

    这同样适用于我的web应用程序,如果它不适用于您,那么我猜您的JavaBean代码中有错误

  3. # 3 楼答案

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

  4. # 4 楼答案

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

  5. # 5 楼答案

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

    因此,要使用的正确clientId将是粗体加上目标容器的id(在本例中显示)

    tabs:insTable:display