Search code examples
jakarta-eejsf-2facelets

How to avoid namespace collision when including identical XHTML JSF 2.0 files


In my JSF 2.0 project, I get a name-space collision when I include two identical XHTML files in a single parent XHTML file. My goal is to create a dashboard of identical controls on a single page, with each control representing a separate context.

For example, in the following JSF file, called parent.xhtml (say), I include two "dashboard" facelets:

<ui:define name="body">
    <p:panel id="ONE"  >
        <ui:include src="raceboardunit.xhtml"/>
    </p:panel>

    <p:panel id="TWO" >
        <ui:include src="raceboardunit.xhtml"/>
    </p:panel>
</ui:define>

To be simple, say the child facelet "raceboardunit.xhtml" is the following:

<ui:composition xmlns:ui="http://java.sun.com/jsf/facelets"
            xmlns:h="http://java.sun.com/jsf/html"
            xmlns:f="http://java.sun.com/jsf/core" 
            xmlns:p="http://primefaces.prime.com.tr/ui">

    <p:panel header="Dashboard" >
        <h:panelGrid columns="2">
            <h:outputLabel value="Event" for="idEvent}" />
            <h:outputLabel value="#raceBoardController.name}" id="idEvent" />
        </h:panelGrid>
    </p:panel>
</ui:composition>

The problem is the collision involving the output label id="idEvent", since in the parent.xhtml file, the two inclusions of the child facelet have an identical named component.

The error is reported as: Component ID idEvent has already been found in the view.

The reason for the error is obvious, but what is the best way to include a variable number of identical components in a JSF 2.0 application that maximises code reuse?


Solution

  • Easiest but ugliest way is to wrap it in a <f:subview> which basically introduces a new UINamingContainer layer.

    <p:panel id="ONE">
        <f:subview id="board1">
            <ui:include src="raceboardunit.xhtml"/>
        </f:subview>
    </p:panel>
    <p:panel id="TWO">
        <f:subview id="board2">
            <ui:include src="raceboardunit.xhtml"/>
        </f:subview>
    </p:panel>
    

    Better is to make it a tag file or a composite component instead.

    Here's an example of how it can look like as a tag file:

    <p:panel id="ONE">
        <my:raceboardunit id="board1" />
    </p:panel>
    <p:panel id="TWO">
        <my:raceboardunit id="board2" />
    </p:panel>
    

    with in tag file

    <h:outputLabel for="#{id}_idEvent" value="Event" />
    <h:inputText id="#{id}_idEvent" value="#{raceBoardController.name}" />
    

    (I think that you oversimplified the example in the question too much that it made no sense; a label referring another label? So I've also fixed it so that it makes somewhat sense.)

    A composite component has basically the same usage as a tag file, but is by itself implicitly already an UINamingContainer, so you wouldn't need to manually prefix the IDs inside the composite component implementation.

    <h:outputLabel for="idEvent" value="Event" />
    <h:inputText id="idEvent" value="#{raceBoardController.name}" />
    

    See also: