Search code examples
jsfjsf-2faceletscomposition

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


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?


Solution

  • 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: