首页 文章

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

提问于
浏览
125

以下代码的灵感来自PrimeFaces DataGrid DataTable教程,并放入 <p:tab><p:tabView> 中_2598670_的_2596861_ . 这是代码的内部部分(从 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”的上下文中找到它

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

5 回答

  • 282

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

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

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

    记住NamingContainer组件并始终为它们提供固定ID

    如果您想通过ajax process / execute / update / render引用的组件位于同一个NamingContainer parent中,那么只需引用它自己的ID .

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

    如果 not 在同一 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接口 . 例如,HtmlForm(_2596999_标记后面的 UIComponent )显示它实现 NamingContainer ,但HtmlPanelGroupUIComponent 标记后面的 UIComponent )没有显示它,因此它没有实现 NamingContainer . Here 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的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 UIComponent#findComponent() javadoc中指定为"search expression":

    搜索表达式由一个标识符(与UIComponent的id属性完全匹配,或者由UINamingContainer#getSeparatorChar字符值链接的一系列此类标识符)组成 . 搜索算法应按如下方式运行,但备用算法可能是只要最终结果相同,就使用:识别将成为搜索基础的UIComponent,只要满足下列条件之一就停止:如果搜索表达式以分隔符开头(称为“绝对” “搜索表达式”,基数将是组件树的根UIComponent . 前导分隔符字符将被剥离,搜索表达式的其余部分将被视为“相对”搜索表达式,如下所述 . 否则,如果此UIComponent是NamingContainer,它将作为基础 . 否则,搜索此组件的父级 . 如果遇到NamingContainer,它将是基础 . 否则(如果没有遇到NamingContainer),根UIComponent将成为基础 . 搜索表达式(可能在上一步中修改)现在是一个“相对”搜索表达式,用于在基本组件的范围内定位具有匹配id的组件(如果有) . 匹配执行如下:如果搜索表达式是一个简单的标识符,则将此值与id属性进行比较,然后递归通过基本UIComponent的facets和children(除非找到后代NamingContainer,否则它自己的facet)和儿童不被搜查) . 如果搜索表达式包含由分隔符分隔的多个标识符,则第一个标识符用于通过上一个项目符号点中的规则定位NamingContainer . 然后,将调用此NamingContainer的findComponent()方法,传递搜索表达式的其余部分 .

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

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

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

    从不使用prependId =“false”

    如果这仍然没有't work, then verify if you aren'使用 <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}" />
    </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> 开始支持它(它只是停止验证它;因此你再也不会面对提到异常的问题了;后来计划了另一个增强修复) .

    这只是't work yet in current MyFaces 2.2.7 and PrimeFaces 5.2 versions. 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渲染HTML,如 <ui:repeat> .

    使用PrimeFaces时,请考虑搜索表达式或选择器

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

    • @this :当前组件

    • @form :parent UIForm

    • @all :整个文件

    • @none :什么都没有

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

    • @parent :父组件

    • @namingcontainer :父亲 UINamingContainer

    • @widgetVar(name) :由给定 widgetVar 标识的组件

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

    PrimeFaces Selectors (PFS),如 @(.someclass) 允许您通过jQuery CSS选择器语法引用组件 . 例如 . 引用HTML输出中具有所有通用样式类的组件 . 如果您需要引用"a lot of"组件,这将特别有用 . 这仅预先确定目标组件在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代码有问题...

  • 0

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

  • 5

    尝试将 update="insTable:display" 更改为 update="display" . 我相信你不能使用类似的表单ID为id添加前缀 .

  • 0

    我知道这已经得到了BalusC的一个很好的答案,但是 here is a little trick I use to get the container to tell me the correct clientId .

    • 删除组件上无效的更新

    • 在您尝试更新的组件中放置一个带有伪造更新的临时组件

    • 点击页面,servlet异常错误会告诉你需要引用的正确客户端ID .

    • 删除虚假组件并在原始更新中放入正确的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"
    

    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

    <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
    

相关问题