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