The problem will be come if your render facet inside NamingContainer.
For example, composite component:
* dataTable
*column
* panelGrid
* facet "caption" : *reference* facet "myfacet" <--
non-child facet
will not work without relocation. But, it would be even worse if you
want to render facet more then once:
* dataTable
* facet "caption" : *reference* facet "myfacet"
*column
* panelGrid
* facet "caption" : *reference* facet "myfacet"
On 09/24/2009 10:17 PM, Ken Paulsen wrote:
Andy Schwartz wrote:
> Hey Ken -
>
> Ken Paulsen wrote:
>>
>>
>> Andy Schwartz wrote:
>>>
>>> This raises a number of questions. Why do we re-parent the facet
>>> component when we want the component to be used by the parent
>>> component as a facet,
>> There is no way to add a facet without it being re-parented. Perhaps
>> we should have changed the behavior to allow non-child components to
>> be facets of a component.
>
> This stuff is so complicated. I am lost already. :-) Can you explain
> what you mean by "allow non-child components to be facets of a
> component"?
Perhaps it's best to explain visually:
Page author defines:
* Page A
* compComp
* facet "myfacet" : component X <-- not a child of panelGrid
Component author defines:
* panelGrid
* facet "caption" : *reference* facet "myfacet" <-- non-child
facet
So instead of re-parenting "component X" from "compComp" to
"panelGrid",
panelGrid maintains it's relationship that the page author defines.
Today's behavior does the re-parenting as soon as you add it to the
facet Map. A proxy-component might be possible that acts like a
"symbolic link" to the page author's component so that the page
author's
original component doesn't get moved.
>>> but not when we want the component to be used by the parent as a
>>> direct child?
>> As you pointed out, there is no way to add a component from the page
>> to a component in the EZComp as a child (only as a facet). If it were
>> possible, under the current JSF code, it too would be re-parented.
>
> Right. I was thinking that ideally composite:insertFacet would cover
> both the insert as a child case as well as the insert as a facet case
> (like ui:insert).
>
>>> Is there a reason why we need to treat the direct child case
>>> differently (not re-parent) than the facet case (re-parent)? Is
>>> there some benefit to having subtly different behavior between these
>>> two cases?
>> Yes, the not-reparent case is not a child. It is an omission and were
>> it to be added,it too would re-parent (unless we changed the rules).
>>
>> The reason the 2 tags are treated differently is this: 1 modifies the
>> component tree; the other occurs during rendering. Two very different
>> phases in JSF.
>
> Sure. That explains how these two tags are different, which I
> understand. What I am still confused about is why these two cases
> needed to be handled differently.
>
>>
>> If we were to explore not re-parenting, we'd probably need to create
>> a ProxyComponent which could be added to the child and delegate
>> everything to the real component which would remain on the parent.
>> That might work nicely.
>
> This sounds fairly close to what composite:renderFacet is doing, right?
Yes, but afaik, only for the "rendering" of the component. For example,
if you ask for the "children" of the component put in as a place holder
for rendering, I don't think you'll get the children defined by the page
author... you'll get an empty list of children b/c there are no children
of the placeholder itself.
>>> While the first case inserts the component as a facet and the second
>>> as a direct child, both have the same semantics - ie. in both cases
>>> the component that is being inserted ends up as a child of the
>>> parent component that the child is being inserted into.
>> Which is wrong. The parent ideally should be the *composite
>> component*. Keep in mind the template is a black box that the page
>> author should not know anything about. So if I do:
>>
>> <my:ezComponent id="foo">
>> <f:facet name="myfacet">
>> <h:outputText id="usercomp" value="here" />
>> </f:facet>
>> </my:ezComponent>
>>
>> So looking at this page excerpt, you'd say the parent of
"usercomp"
>> is "foo". And if foo is a naming container, the clientId would be
>> something like: "...:foo:usercomp".
>>
>> However, if the implementation of the ezcomp component (that the page
>> author has no control over and no know knowledge of) is implemented
>> like this:
>>
>> ...
>> <some:namingcontainer id="hiddenContainer">
>> <ui:insert facet="myfacet" />
>> </some:namingcontainer>
>> ...
>>
>> Then we have problems:
>>
>> 1) The parent is *not* "foo" as the page author expected (and as
>> would be the case with ALL Java-based components).
>>
>> 2) The clienId is not what we expected:
>> "...:foo:...:hiddenContainer:myfacet"
>>
>> 3) Changes that a component author makes to the component will break
>> any code the page author writes that depends on the location or
>> clientId of the facet component.
>>
>
> Right. These are very similar to the issues that I was concerned about
> with #{cc} resolution.
>
> Of course, we are already exposed to these issues with
> composite:insertFacet, eg:
>
> <composite:implementation>
> <f:subview>
> <h:panelGrid>
> <composite:insertFacet name="caption"/>
> </h:panelGrid>
> </f:subview>
> </composite:implementation>
>
> I have mixed feelings about this. When I wrote my previous mail, I
> suppose I was thinking that if we are going to be exposed to this
> behavior in both composite:insertFacet and ui:insert, then I would
> rather just be consistent and lose the composite:renderFacet behavior.
> But I understand your concerns about breaking the page author's (ie.
> composite component user's) abstraction.
>
> Let me think this over some more.
>
> FWIW, another issue that we haven't talked about is the potential for
> id collisions between components that the page author adds to the
> composite component vs. components that the composite component
> defines itself in its implementation.
Yes, good point.
>> Now... was facelets wrong w/ ui:insert? Absolutely not... In facelets
>> they were not making components, they were making pages. The page
>> author had visibility and full control of the UIComponent tree
>> structure that was being created. VERY different than the component
>> scenario.
>
> Okay, but just to be clear.... It sounds like your take is that the
> re-parenting behavior of <ui:insert> is (at least somewhat)
> acceptable, but this behavior in <composite:insertFacet> is wrong. Is
> that right?
Yes, that's my current thinking. I see Facelets (and JSFTemplating)
primarily as a way to construct UIComponent trees. So ui:insert is part
of the templating language that pulls together component tree fragments
in interesting ways.
I do not believe that UIComponent tree changes should happen outside of
the component which is added to the tree. The re-parenting that happens
by composite:insertFacet causes the component tree that the page author
specified to change. This breaks encapsulation of the component model.
>> Which is likely why facelets was never able to create real components.
>>
>>> Actually, Facelets provides an API that is specifically designed to
>>> handle this type of insertion: TemplateClient. The TemplateClient
>>> API allows for clean/efficient insertion of included content into
>>> templates. I believe that ui:define/ui:insert use this to insert
>>> included content directly into the target location, avoiding the
>>> need to re-locate the included components from one parent to
>>> another. This has benefits when re-applying tags over a restored
>>> component tree, since there is no need to repeatedly re-create (or
>>> re-locate) these components.
>> Not sure I follow you. But I "think" you're describing meta-data
that
>> represents the "files" which define the UIComponent tree (aka, VAST).
>> Which is something I have been advocating for and can greatly help.
>> However, once components are instantiated as UIComponents, they do
>> either have to be relocated, copied, or proxied (never seen this done
>> in practice)... so I think there's more to it than what you suggest.
>
> There is definitely more to it. My comment was pretty vague. :-)
>
> I need to take a closer look at how tag handler re-execution is
> handled now that we've got partial state saving in place. In the old
> days we used to re-execute tags (both JSP and Facelets) against the
> restored component tree on postbacks. If components were moved around
> since the tags were originally executed, this could lead to
> complications when attempting to re-apply tags to an existing
> component tree - eg. subtrees that were moved from one parent to
> another might be re-populated, leading to unnecessary component
> creation. The TemplateClient solution avoids this, since inserted
> component subtrees are only ever populated to one parent (the target
> of the insertion).
>
> It is possible that this is no longer an issue. I'll poke around and see.
>
>
>>>
>>> If we had the ability to revisit the design now, my preferred
>>> solution would be to:
>>>
>>> 1. Deprecate/remove composite:renderFacet.
>>> 2. Re-spec composite:insertFacet to match the behavior of ui:insert.
>> I think we could do this if we can also ensure that components are
>> NOT re-parented. Then we could provide the correct behavior AND
>> provide a nice syntax for component authors. However, I don't envy
>> the person (Ryan) that has to implement this. :)
>
> I am amazed that Ryan doesn't hate me by now. Well, I hope not anyway.
> ;-)
You should hear the things he says! Ok... not really. ;-) Not sure how
he feels, but I appreciate your comments a lot. :)
>> There are a lot of tags and code that would have to have special-case
>> stuff to make this happen.
>
> Yep. So just to make sure I understand where you stand... Are you
> saying that you agree that we could handle these two cases:
>
> <!-- Insert "foo" facet into the component tree as a facet -->
> <composite:implementation>
> <h:panelGrid>
> <f:facet name="caption">
> <composite:insertFacet name="foo"/>
> </f:facet>
> </h:panelGrid>
> </composite:implementation>
>
>
> <!-- Insert "foo" facet into the component tree as a (non-facet) child
> -->
> <composite:implementation>
> <h:panelGroup>
> <composite:insertFacet name="foo"/>
> </h:panelGroup>
> </composite:implementation>
>
>
> With a single tag? But that you prefer that the tag does not actually
> perform re-parenting, ie. that the parent for the inserted component
> is the composite component itself in both cases?
Yes, that is what I was trying to say. I understand that may be much
easier to *say* than implement, though, as it poses several challenges
-- many of which I probably haven't thought through yet (such as how
id's are resolved if it is inserted 2x or more).
>
> FWIW, even if we do not have the ability to make changes along these
> lines now, I am still interested in understanding/working through
> these problems so that we have a firm grasp of any
> limitations/complications present in our current APIs.
I agree, I like to think through these things as well for the sake of
understanding it better.
Thanks!
Ken
>
> Andy
>
>