Avoiding duplicate ids when reusing facelets compositions in the same naming container

Daniel Loiterton picture Daniel Loiterton · Feb 4, 2014 · Viewed 8.3k times · Source

I have a <ui:composition> that contains a few elements with explicit ids and some ajax events which reference these ids for partial processing/updating. I encapsulated this fragment of xhtml inside the composition simply so I could use it in a few different places without having to duplicate the code. However, when I use the composition (with <ui:include>) more than once inside a page, I get duplicate id exceptions. It seems JSF is not wrapping each composition inside its own naming container (like <ui:component> does).

Is there a simple way to wrap my composition inside its own naming container? Or do I have to use a composite component every time I want to reuse xhtml fragments inside a common naming container?

Answer

BalusC picture BalusC · Feb 5, 2014

Depending on the purpose of the <ui:include> template, you've several options:

  1. Use <f:subview>. It creates another NamingContainer context (like as <h:form>, <h:dataTable>, and friends all do):

    <f:subview id="top">
        <ui:include src="/WEB-INF/includes/some.xhtml" />
    </f:subview>
    ...
    <f:subview id="bottom">
        <ui:include src="/WEB-INF/includes/some.xhtml" />
    </f:subview>
    

    The components definied in some.xhtml will end up getting respectively top: and bottom: prefix in their ID.


  2. Turn it into a tagfile which requires an id attribute.

    <my:some id="top" />
    ...
    <my:some id="bottom" />
    

    And use that ID to prefix the ID of the components in the composition.

    <ui:composition 
        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"
    >
        ...
        <h:someComponent id="#{id}_some" />
        <h:otherComponent id="#{id}_other" />
        ...
    <ui:composition>
    

  3. Turn it into a composite component. Composite components are inherently already a NamingContainer, so their id attribute is optional. Basically, replace

    <ui:composition 
        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"
    >
        ...
    <ui:composition>
    

    by

    <ui:component 
        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:cc="http://java.sun.com/jsf/composite"
    >
        <cc:interface>
            <!-- This is optional. -->
        </cc:interface>
        <cc:implementation>
            ...
            <h:someComponent id="some" />
            <h:otherComponent id="other" />
            ...
        </cc:implementation>
    <ui:component>
    

    This way you can use it as follows:

    <my:some id="top" />
    ...
    <my:some id="bottom" />
    

    The components definied in <cc:implementation> will end up getting respectively top: and bottom: prefix in their ID (note again, the composite component's id attribute is optional, JSF will otherwise autogenerate one).


See also: