I read this and thought "darn it, I *remember* that discussion, now that
he brings it up". And then, apparently, completely forgot that we
added this functionality. My apologies.
The switchlist example predates the naming change, going back to the
dark days when we were just doing simple substitution into the JS function.
Other comments inline.
On 5/25/09 3:43 PM, Andy Schwartz wrote:
<snip>
This will result in the search expression "form1:switchlist:list1" being
resolved relative to the nearest naming container, which in this case is
the switchlist composite component itself. Since there is no
"form1:switchlist:list1" component within the composite component naming
container, findComponent() would return null, and the <f:ajax> tag
should log a warning.
Since that resolving code is only called by different function that
immediately fails if it doesn't find the id, a warning seems
unnecessary. An expanded error message was used instead.
However, instead of failing/logging a warning, we've got some
code in
AjaxBehaviorRenderer.findComponent() that does the following:
> UIComponent resolvedComponent = component.findComponent(expr);
> if (resolvedComponent == null) {
> // not found using a relative search, try an absolute search
> resolvedComponent = component.findComponent(':' + expr);
> }
The bonus absolute search is not part of the spec - ie. is not typical
findComponent() id resolution behavior. This code should be removed and
replaced by a warning. (We should be able to make the warning clearer by
identifying the naming container that provided the scope of the search.
Logged as bug 1134
Fix checked in.
In any case, when switching over to:
> <f:ajax execute="@this list1" render="list1 list2"/>
Everything works as expected. We definitely should update this demo to
use this simpler, more spec-compliant solution.
Done. Much cleaner.
In order to try to reproduce the problem that David was seeing, I
decided to see whether I could update the "reload" button on the main
page from within the composite component. So, first I gave the reload
button an id:
> <h:commandButton value="reload" type="submit"
id="reload"/>
And then I added a new render target inside of our <f:ajax> tag (inside
of the composite component):
> <f:ajax execute="@this list1" render="list1 list2
> :#{cc.parent.clientId}:reload"/>
Note that I used the leading ":" character to indicate that this is an
absolute id.
I was thinking that "#{cc.parent}" would resolve to the composite
component's immediate parent, which in this case happens to be the
"form1" component. As such, I figured that "#{cc.parent.clientId}"
would
give me "form1", and ":#{cc.parent.clientId}:reload" would produce
the
absolute id ":form1:reload", which should target the outer reload button
(leaving aside the question of whether this is a good thing to do or not).
However, when I ran the test case, I hit the following error:
> <f:ajax> contains an unknown id '::reload'
Okay, that's weird. So this means that "#{cc.parent.clientId}" is not
resolving to the parent component's client id, but instead to null/empty
string. Huh.
After poking around some more, I found my way to the
com.sun.faces.el.CompositeComponentAttributesELResolver, which provides
some special handling for certain properties on the "cc" object,
including the "parent" property:
> if (COMPOSITE_COMPONENT_PARENT_NAME.equals(propertyName)) {
> UIComponent c = (UIComponent) base;
> context.setPropertyResolved(true);
> UIComponent ccParent = UIComponent.getCompositeComponentParent(c);
> return ccParent;
> }
And, of course, UIComponent.getCompositeComponentParent() does not
return the composite component's parent, but instead returns the nearest
ancestor composite component. Which, in this particular demo, is...
null. Yuck. Okay, in retrospect, rather than re-defining the meaning of
"parent" here, we should have introduced a new derived property, eg.
"compositeParent", or something along those lines. (Unfortunately the
current behavior is defined by the spec - section 5.6.2.2, so too late
to do anything about this for 2.0.)
After thinking over this for a while, a lack of a real "parent" object
may not be a bad thing.
After all, we'll want to tell people not to make components that
invisibly modify the using page. If you want to modify the using page,
you should have to explicitly make the user say so, and mark the id's to
be changed, or else you're going to make stuff that's impossible to
maintain.
<snip>
Jim