Leonardo,
I agree that you can get rid of the restriction in a very limited case,
but is it worthwhile to make such a change? The reason the composite
components are NamingContainers is to ensure isolation from the
consuming page. Your solution to avoiding id conflicts in this case was
to not set any ids in your composite component definition. This
precludes the definition from EVER using any id-based services, such as
<f:ajax> within its definition. In addition, your proposal does nothing
to address abstraction failure issues regarding whether a composite
component implementation happens to use NamingContainers as part of its
implementation.
I do not consider the initial findComponent() failure a bug. Page
authors using components need to know whether they are NamingContainers
or not. Good IDEs should be helping you out in this case. In many
ways, it is easier for page authors to remember whether a component is a
composite component or not if all of the composite components are
NamingContainers.
The abstraction failure is that findComponent() should always work in
terms of the document that the page author is working in. In the case
of composite components, this means that component references in the
page consuming the composite component should stay the same regardless
of whether a composite component uses NamingContainer in its
implementation or not.
Fixing findComponent() should not break invokeOnComponent() and
visitTree() as we are not talking about changing the structure of the
returned clientIds for the child components from the consuming page
after they have been relocated into the composite component.
-- Blake Sullivan
On 7/1/11 4:11 PM, Leonardo Uribe wrote:
> Hi
>
> BS> Isn't what you are complaining about really a failure abstraction in JSF's
> BS> handling of composite components?
>
> In my understanding, the idea with the introdution of composite
> components was create a clean interface to replace facelets user tags,
> with typed properties and many other features to make easier write
> components.
>
> Unfortunately, there are some cases where you want to replace some
> facelets code with a composite component, but you can't because the
> composite component base class is a NamingContainer. Previously, I
> described the most typical use case and prove that it is possible to
> get rid of that restriction. My position about this topic is since
> there is nothing that enforce that, it is possible to assume the
> opposite and at least do it in MyFaces Core.
>
> It can be considered a failure abstraction, but the fact is that there
> is no restriction to implement it, so I'm considering it an
> implementation failure.
>
> BS> Shouldn't we be fixing findComponent to
> BS> work correctly instead of adding a workaround that only works if no ids are
> BS> set on the contents of the composite component?
>
> I don't believe a fix on findComponent could do the trick, because
> clientId structure is used for invokeOnComponent and visitTree
> algorithm, so any change there will break partial state saving
> algorithm.
>
> It is evident that by default a composite component should be a
> NamingContainer, but allow any component to be a composite component
> base does not harm, as long as the developer respect the rule of no
> ids set inside composite component content.
>
> I still have to do some tests to know how far can we go in that direction.
>
> Leonardo Uribe
>
>> -- Blake Sullivan
>>
>> On 6/28/11 6:15 PM, Leonardo Uribe wrote:
>>> 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
>>