Search code examples
ajaxjsfjsf-2faceletscomposite-component

Error: <f:ajax> contains an unknown id when used with composite:insertChildren


I defined a composite component (actually a few) and when I try to re-render a component inserted with I get the error mentioned:

<f:ajax> contains an unknown id ':contentFrm' - cannot locate it in the context of the component j_idt40

If I simply replace:

<et:pageContent formId="contentFrm">

with

<h:form id="contentFrm">
<div>

then everything works fine

Here is the relevant code:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> 
<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:h="http://java.sun.com/jsf/html"
      xmlns:composite="http://java.sun.com/jsf/composite"> 

    <composite:interface>
        <composite:attribute name="styleClass" default="access-box-content alpha omega grid-12" />
        <composite:attribute name="formId" default="#{cc.attrs.id}" />
    </composite:interface>
    <composite:implementation>
        <h:form id="#{cc.attrs.formId}">
            <div class="#{cc.attrs.styleClass}">
                <composite:insertChildren/>
            </div>
        </h:form>
    </composite:implementation>
</html>

This is how I use the composite

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" 
    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<ui:composition id="landing" xmlns="http://www.w3.org/1999/xhtml"
                xmlns:f="http://java.sun.com/jsf/core"
                xmlns:h="http://java.sun.com/jsf/html"
                xmlns:ui="http://java.sun.com/jsf/facelets"
                xmlns:et="http://java.sun.com/jsf/composite/components"
                template="/layout/template.xhtml"
                xmlns:c="http://java.sun.com/jsp/jstl/core">

    <ui:define name="main-content">
        <et:pageTitle title="#{msg.trx_lastTrx}" />
        <et:tabBar formId="currentTrxFrm">    
            <et:tab bean="#{accountTransactionBacking}"
                    prompt="#{msg.trx_currentTrxs}"
                    reRender=":contentFrm"
                    tabId="1"
                    active="true"/>
        </et:tabBar>
        <et:pageContent formId="contentFrm">
                <et:tabContentPanel rendered="#{accountTransactionBacking.selectedTab ==1}">
                    <ui:include src="/app/summary/currentTrxRG.xhtml" rendered="#{accountTransactionBacking.selectedTab ==1}"/>
                </et:tabContentPanel>
        </et:pageContent>
    </ui:define>
</ui:composition>

TIA

Some more tests.. using same component get's the same result.. for example the following code gives similar error (even replacing render="accountSummaryLines" with render=":accountSummaryLines")..

    <?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" 
    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<ui:composition id="landing" xmlns="http://www.w3.org/1999/xhtml"
                xmlns:f="http://java.sun.com/jsf/core"
                xmlns:h="http://java.sun.com/jsf/html"
                xmlns:ui="http://java.sun.com/jsf/facelets"
                xmlns:et="http://java.sun.com/jsf/composite/components"
                template="/layout/template.xhtml"
                xmlns:c="http://java.sun.com/jsp/jstl/core">

    <ui:define name="main-content">
        <et:pageTitle title="#{msg.ls_lastStatement}" />

        <et:tabBar formId="acctSummTabFrm">
            <et:tab/> 
        </et:tabBar>

            <et:pageContent formId="accountButtonsFrm">

                ....

                <et:div styleClass="accountSummaryLines" divId="lastStatementLines">
                    <ui:repeat var="row" value="#{lastStatementBacking.lines.data}">
                        <h:outputText escape="false" value="#{row}" styleClass="grid-12 lastStatementDetail"/><br></br>
                    </ui:repeat>
                </et:div>
                <et:div styleClass="access-box-footer">
                    <h:commandButton styleClass="left" action="#{lastStatementBacking.lines.prevPage}" value="#{msg.buttonPrevPage}" style="float:left;">
                        <f:ajax render="accountSummaryLines" />
                    </h:commandButton>
                    <h:commandButton styleClass="right" action="#{lastStatementBacking.lines.nextPage}" value="#{msg.buttonNextPage}">
                        <f:ajax render="accountSummaryLines" />
                    </h:commandButton>
                </et:div>
            </et:pageContent>
    </ui:define>
</ui:composition>

The only way I got this particular example working is using render="@form" like so..


Solution

  • It cannot be found because the composite component is by itself a NamingContainer. The real valid client ID would be :idOfComposite:contentFrm where idOfComposite is the (auto)generated ID of the composite component itself. If you do a View Source in webbrowser and locate the <form> in question, then you'll see it.

    You need to give the composite component a fixed ID

    <et:pageContent id="contentFrm">
    

    and use exactly this ID on a plain HTML element which wraps the composite's content, e.g. <div> or <span>.

    <composite:implementation>
        <div id="#{cc.id}">
            <h:form>
                <div class="#{cc.attrs.styleClass}">
                    <composite:insertChildren/>
                </div>
            </h:form>
        </div>
    </composite:implementation>