[jsr-314-open] Ajax rendering of components among compositions?
Andy Schwartz
andy.schwartz at ORACLE.COM
Thu May 28 14:49:59 EDT 2009
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
More information about the jsr-314-open-mirror
mailing list