jsr344-experts@javaserverfaces-spec-public.java.net

[jsr344-experts] [jsr-344-experts] should really a composite component requires NamingContainer interface?

From: Leonardo Uribe <lu4242_at_gmail.com>
Date: Tue, 28 Jun 2011 20:15:22 -0500

Hi

In theory, a "non explicit" requeriment for a composite component
class is it should implement NamingContainer interface. Note I said
"non explicit", because in practice there is nothing on the spec that
enforces that. The only mention can be found on facelets taglib doc
for composite:interface, section titled Naming containers within
composite components:

"... Composite components are themselves naming containers so that any
possible id conflicts between inner components and components in the
using page are avoided. However, special care must be taken when using
naming containers in the <composite:implementation> section. ..."

It is true that by default, composite component base class is
UINamingContainer, and the component family should be
"javax.faces.NamingContainer" to get the renderer class.

Let's take a look at this simple example:

<h:form id="frmTst">
        <ui:include src="demoui1.xhtml"/>
        <ui:include src="demoui1.xhtml"/>
        <ui:include src="demoui1.xhtml"/>
</h:form>

demoui1.xhtml

<ui:composition>
    <p><h:commandButton value="Hello World!"/></p>
</ui:composition>

The previous code works in both MyFaces and Mojarra. This is valid
from facelets 1.1.x. The idea is as long as there is no component with
"id" set, the previous code will work. Combinations of c:if and
ui:include could be possible, but this work well only on MyFaces 2.0.x
and upper and facelets 1.1.x. In Mojarra 2.0.x some changes were done
that makes such cases not work correctly (but in my opinion c:if is
something evil, because it causes a lot of nightmares).

Now let's try to convert the previous case into a composite component,
but this time let's create a normal component that does not implement
NamingContainer, because after all the previous snippet followed the
rule that no components with id set were added:

<h:form id="frmTst">
        <test:ccc value="Hello World!"></test:ccc>
        <test:ccc value="Hello World!"></test:ccc>
        <test:ccc value="Hello World!"></test:ccc>
</h:form>

ccc.xhtml

<cc:interface componentType="test.ComponentCCC">
        <cc:attribute name="value" type="java.lang.String" />
</cc:interface>
<cc:implementation>
    <p><h:commandButton value="#{cc.attrs.value}"/></p>
</cc:implementation>

@FacesComponent("test.ComponentCCC")
public class ComponentCCC extends UIOutput
{
    public final static String COMPONENT_TYPE = "test.ComponentCCC";


    @Override
    public String getFamily()
    {
        return UINamingContainer.COMPONENT_FAMILY;
    }
}

In theory, since all ids are generated, everything should work like in
ui:include case. In practice, the previous code works on MyFaces but
does not on Mojarra.

Why somebody could want to do this trick? Because NamingContainer has
its effects, specially with UIComponent.findComponent. Suppose you
want to create a component that works as a panel. You can imagine
something like this:

<h:form prependId="true">
    <y:myPanel>
        <h:inputText id="field1" ... />
        <!-- more components -->
    </y:myPanel>
</h:form>

y.xhtml

<cc:implementation>
    <!-- some javascript markup with html and css -->
    <cc:insertChildren />
    <!-- some javascript markup with html and css -->
<cc:implementation>

The user wants that the final clientId be "field1", but instead he/she
has "[generated id]:field1", and the worst part is if you need some
call to invokeOnComponent or findComponent, you have to set the id of
your composite component and take into account the internals of such
cc to get the component instance.

Basically the only thing we need to do on Mojarra to make it work is
do some small changes on id generation. Note in this case, the xhtml
file related to the composite component will only be used to render
the markup, and other stuff like attached objects should be done like
with normal components: adding the code on the class.

Allow this behavior could be great, because makes possible to write
more JSF typical components as composite components, keeping the
markup on a xhtml file and the logic on the component/renderer
classes.

Note that the trick proposed here is compliant with the spec, just
like the one proposed long time ago related to customize the renderer
instance for a composite component. In fact, I'm looking for a way to
write components more easily, and do this trick for me seems the way
to go.

Should Mojarra fix this issue and enforce better compatibility with
facelets 1.1.x? Any comments about this one? Suggestions are welcome

regards,

Leonardo Uribe