[jsr-314-open] composite:insertFacet target facet name

Ken Paulsen Ken.Paulsen at Sun.COM
Fri Sep 25 01:17:34 EDT 2009



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
>
>




More information about the jsr-314-open-mirror mailing list