-Dan
On Thu, May 28, 2009 at 2:49 PM, Andy Schwartz <andy.schwartz(a)oracle.com>wrote:
Hey Alex -
Alexandr Smirnov wrote On 5/26/2009 5:41 PM ET:
> JSF already has a smart enough code that look components by 'id'. Both
> <h:outputLabel> and <h:message > use it to lookup target component that
> is defined by the 'for' attribute, RichFaces library uses the same
> strategy and it works very well. It would be better to use a same code
> for <f:ajax> tag instead of simple local/global search.
>
>
>
Good point. I agree that ideally we should be consistent across the various
cases where component attributes refer to other components by id. I took a
closer look at the spec/code for the outputLabel "for" attribute. The spec
is unfortunately vague (and possibly incorrect). The "for" tag doc simply
says:
Client identifier of the component for which this element is a label.
>
Adam logged the following related spec issue on this a couple of years ago:
https://javaserverfaces-spec-public.dev.java.net/issues/show_bug.cgi?id=266
In which Adam says:
The Javadoc for UIMessage and TLDDOC for h:message describes it as one
> that takes a clientId. This was
> never the intent - this should always have been a relative ID, exactly as
> in h:outputLabel. As specified,
> h:message is incredibly difficult to use. IMO, this is an errata, not a
> spec change, though for backwards
> compatibility I'd recommend continuing to look for messages in addition as
> if "for" were for clientIds.
>
One thing that is odd is that this implies that outputLabel is correctly
specified, though as far as I can tell, both outputLabel and message share
the same "for" doc. (At least the tag doc - perhaps I am missing other doc
in the spec itself.)
Looking at the implementations, MyFaces does the following:
- Calls findComponent() using the outputLabel/message component as the base
component.
- If a component is found, return the id.
- If the component is found, attempt to construct an id manually (via
string manipulation).
So, this is pretty much identical to our f:ajax execute/render id behavior,
at least in the case where the id corresponds to a component that can be
found in the component tree. That is:
<h:outputLabel for="foo"/>
And:
<f:ajax execute="foo"/>
Will find the same components when running in MyFaces. (Same goes for the
id ":foo").
Mojarra starts off similar...
- Calls findComponent() using the outputLabel/message component as the base
component.
So for cases where a relative id is specified in the same naming container,
or an absolute id is specified, both <f:ajax> and <h:outputLabel> behave the
same in Mojarra. However, in the event that the findComponent() call does
not find the component, Mojarra then goes on to:
- Walk up the ancestor chain.
- For each component in the ancestor chain, call findComponent().
- If we reach the root and still haven't found the matching component,
recursively walk over the entire component tree looking for
NamingContainers, and call findComponent on each NamingContainer.
Umm... Yikes. That's a lot of component tree walking!
So, a few issues here:
1. We need a spec clarification
MyFaces and Mojarra are providing different behaviors... As things
currently stand, the following code:
<f:subview id="bar">
<h:outputLabel for="foo">
</f:subview>
<h:inputText id="foo"/>
Is not going to be portable across JSF implementations.
2. The idea of doing so many findComponent() calls worries me.
A case that we should be able to handle efficiently is having an <f:ajax>
tag attached to a component (eg. an <h:commandButton>) that is being stamped
out repeatedly (eg. in an <h:data> component). It is possible that the
<f:ajax> execute/render lists may contain multiple execute/render ids each.
In this case, we would end up doing possibly many findComponent() calls
each time the component is stamped out - ie. for each row in a table.
Perhaps with very small component trees this may be acceptable, but I can
easily see this becoming inefficient as the size of the component tree
grows.
Personally I would like to see an approach that optimizes common cases to
avoid unnecessary findComponent() calls. For example, there may be cases
where we could:
- Take the id that we are provided.
- Perform some string manipulation to derive the desired id (eg. append the
id to the nearest naming container's client id).
- Return the derived id. (No findComponent() calls).
Or, if we want to provide diagnostic capabilities:
- When project stage is development, after we have derived the id, call
findComponent() to verify that the referenced component is present.
We may not be able to derive ids in this fashion for all case, but it seems
to me that this should work okay for simple "relative" ids - eg.
execute="foo".
3. We should re-evaluate our component referencing mechanisms for 2.1.
For example, I believe that we should provide a way to explicitly address
components in ancestor naming containers, rather than rely on an expensive
implicit search mechanism. Trinidad provides a way to do this - we use the
"::" prefix to indicate that we should pop up one level in the naming
container hierarchy (similar to ".." for directory paths).
Dan mentioned earlier in the thread:
Frankly, for JSF 2.1, I would like to see us go to an XPath-like syntax
> (or jQuery) to find components because component IDs in JSF just plain suck.
>
Not sure what the best solution is, but I agree with Dan that we should
take another look at this.
4. Does <f:ajax> actually need to perform id processing/encoding at render
time?
<f:ajax>/AjaxBehavior currently send all execute/render ids to the client -
which means:
- Lots of findComponent() calls at render time.
- Lots of content bloating.
However, as Alex has suggested in the past, this may be unnecessary.
Instead of sending these ids back and forth to/from the client, we should
be able to enhance our Behavior mechanism to give the Behavior that
triggered a postback the first crack at processing the request... So, for
example, this would allow AjaxBehavior to enhance the PartialViewContext's
execute/render lists early on during the Faces lifecycle (when processing a
postback). No need to send the ids to the client at all. The cost of doing
this is one extra tree visit, which seems like a reasonable trade-off.
Let's revisit this one 2.1.
Andy
NOTE: While I make a strong effort to keep up with my email on a daily
basis, personal or other work matters can sometimes keep me away
from my email. If you contact me, but don't hear back for more than a week,
it is very likely that I am excessively backlogged or the message was
caught in the spam filters. Please don't hesitate to resend a message if
you feel that it did not reach my attention.