On 09/15/2009 03:34 PM, Andy Schwartz wrote:
Ryan, All -
Breaking this topic off into a separate thread, though I am impressed
with the length of the "<h:dataTable> binding vs. ui:repeat" thread. :-)
Ryan Lubke wrote:
> On 9/7/09 6:06 PM, Andy Schwartz wrote:
>> Thanks Lincoln. I haven't had time to debug this, but I have a
>> theory about what might be happening. In your sample:
>>
>>> <a:editText value="#{cc.attrs.task.text}"
>>> rendered="#{!cc.attrs.disabled}">
>>> *<f:actionListener for="submit"
>>> action="#{taskController.saveTaskAjax(cc.attrs.story,
>>> cc.attrs.task)}" /> *
>>> </a:editText>
>>
>> We expect "cc" to resolve to the containing composite component (ie.
>> to the <socialpm:taskBlock> component). I wouldn't be surprised if
>> what is actually happening is that "cc" is being resolved to the
>> <a:editText> composite component instead.
> Yep, that's what is happening.
>> One reason why I suspect this might be happening is that I know that
>> Ryan has investigated/resolved similar problems not too long ago.
> The problem we resolved was the passing of #{cc.attrs} attributes
> between nested composite components.
> For this case, the spec recommends using cc.parent.attrs.story and
> cc.parent.attrs.task, where parent resolves
> to the nearest composite component parent of the current composite
> component.
I am concerned about this behavior. It isn't clear to me whether the
spec is explicit about this behavior, or whether this is something
that happens to fall out of the implementation. Either way, the
current behavior seems counterintuitive to me, and, well... wrong.
My feeling is that when a composite component author uses a #{cc}
expression in a composite component implementation, eg:
<composite:implementation>
<h:outputText value="#{cc.attrs.value}">
</composite:implementation>
The correct behavior (the behavior expected by the composite component
author) is to resolve the #{cc} expression relative to where the
expression is defined - not relative to where it happens to end up in
the component tree. So, for example, I believe that these three uses
of the #{cc.attrs.value} expression should all evaluate to the same
value:
<composite:implementation>
<!-- 1. Expression directly in the implementation -->
<h:outputText value="#{cc.attrs.value}">
<!-- 2. Expression in non-composite component facet -->
<bar:someJavaComposite>
<f:facet name="someFacet">
<h:outputText value="#{cc.attrs.value}">
</f:facet>
</bar:someJavaComponent>
<!-- 3. Expression in composite component facet -->
<foo:someCompositeComponent>
<f:facet name="someFacet">
<h:outputText value="#{cc.attrs.value}">
</f:facet>
</foo:someCompositeComponent>
</composite:implementation>
The fact that the #{cc.attrs.value} expression evaluates the same in
cases #1 and #2 but not in case #3 is non-obvious and is going to be
confusing for our users. The fact that Lincoln and I were confused
about this is a good indication that others will be as well.
Complete agree with
that point. Especially in the case of using
third-party libraries, where vendor could decide to change component
implementation from java to composite or vice versa that can break other
composite components depended of the original library.
I am not happy with the work around of using #cc.parent.attrs, since
this means that:
1. The user must recognize that the component in question is a
composite component and code the expression differently. While at the
moment it is fairly easy to determine whether a particular component
is a composite or not (by looking at the namespace declaration), I
think that we should strive to blur these lines rather than reinforce
them. For example, I would like to see the ability to define a single
tag library that consists of both composite and non-composite
components, making the choice of component implementation more
transparent to the user.
Yes, that is obvious wishlist for component library
vendors.
Unfortunately, it is not possible now to mix Java and composite
components in the same library ( at least, this is not trivial to do in
the implementation-independent way.
2. Once the user has coded the expression in a composite
component-specific way, this introduces a dependency on the fact that
the component in question is a composite component - a dependency that
shouldn't be necessary for this particular case. Such dependencies
inhibit flexibility. If in the future the provider of the composite
component decides to switch to a Java-based component, this will break
any code (or EL expression) that assumes that the component is a
composite. We should keep such dependencies to a minimum.
3. This also introduces a subtle dependency on the composite
component's implementation. What if the composite component
implementation passes the expression along to yet another nested
composite component? Does the user now need to specify an expression
that pops out multiple levels of parents?
While it is certainly trickier to deal with from an implementation
perspective, I believe that it is very important that we re-consider
the current behavior and consider spec'ing that #{cc} expressions
always resolve relative to the context in which they are defined. I
feel that the current behavior violates the principle of least
surprise (both Lincoln and I were surprised) and also breaks
encapsulation (ie. introduces implementation-specific dependencies).
Leaving aside questions of how we might implement my preferred
behavior for the moment... Does anyone have comments on which
behavior makes sense from a spec/end user perspective?
Andy