Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

PrimeFaces/JSF component ID not found from within p:datatable

I'm just starting with PrimeFaces 3.3.1 coming off RichFaces 3 and 4.

I have a datatable with the structure:

            <f:facet name="header">
                <h:outputText value="Employees" />
            </f:facet>

            <p:column sortBy="#{emp.lastName}">
                <f:facet name="header">
                    <h:outputText value="Last Name" />
                </f:facet>
                <h:outputText value="#{emp.lastName}" />
            </p:column>

            <p:column>
                <f:facet name="header">
                    <h:outputText value="First Name" />
                </f:facet>
                <h:outputText value="#{emp.firstName}" />
            </p:column>

            ...

            <p:column>
                    <p:commandButton icon="ui-icon ui-icon-trash"
                                     value="Remove"
                                     process="@this"
                                     update="employee-remove-dialog"
                                     oncomplete="employeeRemoveDialog.show();">
                        <f:setPropertyActionListener target="#{employeeManager.currentEmployee}" value="#{emp}" />
                    </p:commandButton>
            </p:column>
        </p:dataTable>

        <p:dialog header="Remove Employee"
                  modal="true"
                  appendToBody="true"
                  widgetVar="employeeRemoveDialog"
                  id="employee-remove-dialog">
            <h:outputText value="Remove employee #{employeeManager.currentEmployee.fullName}?" />
            <f:facet name="footer">
                <p:commandButton icon="ui-icon ui-icon-check"
                                 value="OK"
                                 action="#{employeeManager.deleteEmployee}"
                                 process="@this"
                                 update="employee-list"
                                 oncomplete="employeeRemoveDialog.hide();" />
                <p:commandButton icon="ui-icon ui-icon-close"
                                 value="Cancel"
                                 onclick="employeeRemoveDialog.hide();"
                                 ajax="false"
                                 immediate="true" />
            </f:facet>
        </p:dialog>

    </h:form>

However PrimeFaces throws an exception:

09:36:08,961 SEVERE [javax.enterprise.resource.webcontainer.jsf.application] (http-localhost-127.0.0.1-8080-1) Error Rendering View[/employeeList.xhtml]: javax.faces.FacesException: Cannot find component with identifier "employee-remove-dialog" referenced from "j_idt30:employee-list:0:j_idt41".
    at org.primefaces.util.ComponentUtils.findClientIds(ComponentUtils.java:251) [primefaces-3.3.1.jar:]
    at org.primefaces.util.AjaxRequestBuilder.addIds(AjaxRequestBuilder.java:102) [primefaces-3.3.1.jar:]
    at org.primefaces.util.AjaxRequestBuilder.update(AjaxRequestBuilder.java:90) [primefaces-3.3.1.jar:]
    at org.primefaces.renderkit.CoreRenderer.buildAjaxRequest(CoreRenderer.java:195) [primefaces-3.3.1.jar:]
    at org.primefaces.component.commandbutton.CommandButtonRenderer.encodeMarkup(CommandButtonRenderer.java:74) [primefaces-3.3.1.jar:]
    at org.primefaces.component.commandbutton.CommandButtonRenderer.encodeEnd(CommandButtonRenderer.java:49) [primefaces-3.3.1.jar:]
    at javax.faces.component.UIComponentBase.encodeEnd(UIComponentBase.java:875) [jboss-jsf-api_2.1_spec-2.0.1.Final.jar:2.0.1.Final]
    at com.sun.faces.renderkit.html_basic.HtmlBasicRenderer.encodeRecursive(HtmlBasicRenderer.java:312) [jsf-impl-2.1.7-jbossorg-2.jar:]
    at com.sun.faces.renderkit.html_basic.GridRenderer.renderRow(GridRenderer.java:185) [jsf-impl-2.1.7-jbossorg-2.jar:]
    at com.sun.faces.renderkit.html_basic.GridRenderer.encodeChildren(GridRenderer.java:129) [jsf-impl-2.1.7-jbossorg-2.jar:]
    at javax.faces.component.UIComponentBase.encodeChildren(UIComponentBase.java:845) [jboss-jsf-api_2.1_spec-2.0.1.Final.jar:2.0.1.Final]
    at javax.faces.component.UIComponent.encodeAll(UIComponent.java:1779) [jboss-jsf-api_2.1_spec-2.0.1.Final.jar:2.0.1.Final]
    at javax.faces.component.UIComponent.encodeAll(UIComponent.java:1782) [jboss-jsf-api_2.1_spec-2.0.1.Final.jar:2.0.1.Final]
    at org.primefaces.component.datatable.DataTableRenderer.encodeRegularCell(DataTableRenderer.java:780) [primefaces-3.3.1.jar:]
    at org.primefaces.component.datatable.DataTableRenderer.encodeRow(DataTableRenderer.java:741) [primefaces-3.3.1.jar:]
    at org.primefaces.component.datatable.DataTableRenderer.encodeTbody(DataTableRenderer.java:645) [primefaces-3.3.1.jar:]
    at org.primefaces.component.datatable.DataTableRenderer.encodeRegularTable(DataTableRenderer.java:248) [primefaces-3.3.1.jar:]
    at org.primefaces.component.datatable.DataTableRenderer.encodeMarkup(DataTableRenderer.java:220) [primefaces-3.3.1.jar:]
    at org.primefaces.component.datatable.DataTableRenderer.encodeEnd(DataTableRenderer.java:107) [primefaces-3.3.1.jar:]
    at javax.faces.component.UIComponentBase.encodeEnd(UIComponentBase.java:875) [jboss-jsf-api_2.1_spec-2.0.1.Final.jar:2.0.1.Final]
    at javax.faces.component.UIComponent.encodeAll(UIComponent.java:1786) [jboss-jsf-api_2.1_spec-2.0.1.Final.jar:2.0.1.Final]
    at javax.faces.render.Renderer.encodeChildren(Renderer.java:168) [jboss-jsf-api_2.1_spec-2.0.1.Final.jar:2.0.1.Final]
    at javax.faces.component.UIComponentBase.encodeChildren(UIComponentBase.java:845) [jboss-jsf-api_2.1_spec-2.0.1.Final.jar:2.0.1.Final]
    at org.primefaces.renderkit.CoreRenderer.renderChild(CoreRenderer.java:55) [primefaces-3.3.1.jar:]
    at org.primefaces.renderkit.CoreRenderer.renderChildren(CoreRenderer.java:43) [primefaces-3.3.1.jar:]
    at org.primefaces.component.layout.LayoutUnitRenderer.encodeEnd(LayoutUnitRenderer.java:51) [primefaces-3.3.1.jar:]
    at javax.faces.component.UIComponentBase.encodeEnd(UIComponentBase.java:875) [jboss-jsf-api_2.1_spec-2.0.1.Final.jar:2.0.1.Final]
    at javax.faces.component.UIComponent.encodeAll(UIComponent.java:1786) [jboss-jsf-api_2.1_spec-2.0.1.Final.jar:2.0.1.Final]
    at javax.faces.component.UIComponent.encodeAll(UIComponent.java:1782) [jboss-jsf-api_2.1_spec-2.0.1.Final.jar:2.0.1.Final]
    at javax.faces.component.UIComponent.encodeAll(UIComponent.java:1782) [jboss-jsf-api_2.1_spec-2.0.1.Final.jar:2.0.1.Final]
    at javax.faces.component.UIComponent.encodeAll(UIComponent.java:1782) [jboss-jsf-api_2.1_spec-2.0.1.Final.jar:2.0.1.Final]
    at com.sun.faces.application.view.FaceletViewHandlingStrategy.renderView(FaceletViewHandlingStrategy.java:402) [jsf-impl-2.1.7-jbossorg-2.jar:]
    at com.sun.faces.application.view.MultiViewHandler.renderView(MultiViewHandler.java:125) [jsf-impl-2.1.7-jbossorg-2.jar:]
    at javax.faces.application.ViewHandlerWrapper.renderView(ViewHandlerWrapper.java:288) [jboss-jsf-api_2.1_spec-2.0.1.Final.jar:2.0.1.Final]
    at com.sun.faces.lifecycle.RenderResponsePhase.execute(RenderResponsePhase.java:121) [jsf-impl-2.1.7-jbossorg-2.jar:]
    at com.sun.faces.lifecycle.Phase.doPhase(Phase.java:101) [jsf-impl-2.1.7-jbossorg-2.jar:]
    at com.sun.faces.lifecycle.LifecycleImpl.render(LifecycleImpl.java:139) [jsf-impl-2.1.7-jbossorg-2.jar:]
    at javax.faces.webapp.FacesServlet.service(FacesServlet.java:594) [jboss-jsf-api_2.1_spec-2.0.1.Final.jar:2.0.1.Final]
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:329) [jbossweb-7.0.13.Final.jar:]
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:248) [jbossweb-7.0.13.Final.jar:]
    at org.jboss.weld.servlet.ConversationPropagationFilter.doFilter(ConversationPropagationFilter.java:62) [weld-core-1.1.5.AS71.Final.jar:2012-02-10 15:31]
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:280) [jbossweb-7.0.13.Final.jar:]
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:248) [jbossweb-7.0.13.Final.jar:]
    at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:275) [jbossweb-7.0.13.Final.jar:]
    at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:161) [jbossweb-7.0.13.Final.jar:]
    at org.jboss.as.jpa.interceptor.WebNonTxEmCloserValve.invoke(WebNonTxEmCloserValve.java:50) [jboss-as-jpa-7.1.1.Final.jar:7.1.1.Final]
    at org.jboss.as.web.security.SecurityContextAssociationValve.invoke(SecurityContextAssociationValve.java:153) [jboss-as-web-7.1.1.Final.jar:7.1.1.Final]
    at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:155) [jbossweb-7.0.13.Final.jar:]
    at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:102) [jbossweb-7.0.13.Final.jar:]
    at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:109) [jbossweb-7.0.13.Final.jar:]
    at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:368) [jbossweb-7.0.13.Final.jar:]
    at org.apache.coyote.http11.Http11Processor.process(Http11Processor.java:877) [jbossweb-7.0.13.Final.jar:]
    at org.apache.coyote.http11.Http11Protocol$Http11ConnectionHandler.process(Http11Protocol.java:671) [jbossweb-7.0.13.Final.jar:]
    at org.apache.tomcat.util.net.JIoEndpoint$Worker.run(JIoEndpoint.java:930) [jbossweb-7.0.13.Final.jar:]
    at java.lang.Thread.run(Unknown Source) [rt.jar:1.7.0_04]

The ID cannot be found. Hmm.... I basically copied the structure from RichFaces. There the row button to delete or edit the row doesn't need the :employee-form:... prefixing. I wonder why.

When giving the form an ID and prefixing the PF component references with the ':' syntax the code runs as expected:

    <h:form id="employee-form">
        <p:dataTable ...>
            ...
            <p:column>
                <h:panelGrid ...>
                    <p:commandButton ...
                                     update=":employee-form:employee-remove-dialog"
                                     ...>
                        ...
                    </p:commandButton>
                </h:panelGrid>
            </p:column>
        </p:dataTable>
        <p:dialog ...
                  id="employee-remove-dialog">
        </p:dialog>
    </h:form>

Q:

Why does a p:datatable need to prefix the root JSF IDs? Maybe it's my code, but the example posted seems minimal to me. In any case, using this will result in possibly long IDs all over the application.

What am I doing wrong?

PS: I'm on JBoss AS 7.1.1.Final, Mojarra 2.1.7, PF 3.3.1

like image 952
Kawu Avatar asked Aug 07 '12 11:08

Kawu


1 Answers

PrimeFaces uses the standard JSF algorithm as provided by UIComponent#findComponent() to find components by a given client ID. The algorithm is in detail described in the aforelinked javadoc. Here's an extract of relevance:

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.

RichFaces uses the same algorithm "with some additional exceptions".

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

Those additional exceptions are nowhere in detail described, but it's well known that relative component IDs (i.e. those not starting with :) are not only searched in the context of the closest parent NamingContainer, but also in all other NamingContainer components in the same view (which is a relatively expensive job by the way).

like image 50
BalusC Avatar answered Oct 13 '22 21:10

BalusC