[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