[jsr-314-open] Ajax rendering of components among compositions?

David Geary clarity.training at GMAIL.COM
Mon May 25 19:16:22 EDT 2009


2009/5/25 Andy Schwartz <andy.schwartz at oracle.com>

>  Gang -
>
> I spent the last couple of hours testing out various composite
> component/f:ajax/id-related use cases, using the ajax-switchlist demo
> (switchlistAjaxComponent.xhtml) as my base test case.  The ajax-switchlist
> component hierarchy looks like this (simplified down to significant parts):
>
> <h:body>
>   <h:form id="form1">
>     <ez:switchlist id="switchlist"/>
>     <h:commandButton value="reload" type="submit"/>
>   </h:form>
> </h:body>
>
>
>
> And <ez:switchlist> composite component contains the following (again,
> stripped down to interesting parts):
>
>   <cc:implementation>
>
>     <h:selectManyListbox id="list1"/>
>
>     <h:commandButton id="move1to2">
>       <f:ajax execute="@this #{cc.clientId}:list1"
> render="#{cc.clientId}:list1 #{cc.clientId}:list2"/>
>      </h:commandButton>
>
>     <h:selectManyListbox id="list2"/>
>
>   </cc:implementation>
>
>
>
> First thing to note...  The execute/render ids are specified in an
> overcomplicated manner.  As I mentioned in email earlier today, the f:ajax
> execute/render ids are spec'ed to adhere to findComponent() id resolution
> semantics.  Since the <f:ajax> behavior is attached to a component that is
> in the same naming container as selectManyListbox components that we are
> targeting, we can simplify from this:
>
>       <f:ajax execute="@this #{cc.clientId}:list1"
> render="#{cc.clientId}:list1 #{cc.clientId}:list2"/>
>
>
>
> To:
>
>       <f:ajax execute="@this list1" render="list1 list2"/>
>
>
>
> Ah, that feels much better.  Actually, this isn't just a simplification...
> this is correct solution.  Unfortunately the original code only happens to
> work due to non-spec compliant implementation behavior.  That is, when we
> specify:
>
>   render="#{cc.clientId}:list1"
>
>
>
> This is the same as specifying:
>
>   render="form1:switchlist:list1"
>
>
>
> Which should have the same behavior as calling:
>
>   move1to2ButtonComponent.findComponent("form1:switchlist:list1");
>
>
>
> 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.
>
> 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.
>
> 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.
>
> 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.)
>
> I thought I might be able to work around this by avoiding the reference to
> the "cc" implicit object - ie. thought I might be able to get this to work
> by using the "component" implicit object instead of the "cc" object, eg:
> ":#{component.parent.parent.clientId}:reload".  But I ended up running into
> the same problem.
>
> So, David - it is possible that you are running into this as well.
>

I believe so.


> It's kind of tough to tell with the auto-generated ids in your content.
> Any chance you could add explicit ids to the naming containers in your
> sample just to make it easier to understand the client ids that are being
> rendered?
>

Okay, sure.


> On a positive note, I was able to manually pass in the reload button id to
> the composite component without any problems.  To do this, I added the
> following to the composite:interface:
>
>     <cc:attribute name="reloadId"/>
>
>
>
> And the following to the composite:implementation:
>
>         <f:ajax execute="@this list1" render="list1 list2
> #{cc.attrs.reloadId}"/>
>
>
>
> And then updated the main page to specify the new attribute:
>
>             <ez:switchlist id="switchlist" reloadId=":form1:reload"/>
>
>
>
> This worked without any problems.
>
> David - since I think we all agree that this is the preferred approach, and
> since there doesn't seem to be any way to get at a composite component's
> actual parent (unless I am missing something), this seems like the right way
> to go for your J1 demo.  If you continue to have problems with this
> approach, maybe cc me when you send Ryan your war.  Don't know how much time
> I'll have to take a look, but I am definitely curious.
>

I agree that it's the preferred approach, however, interestingly enough, I
cannot get it to work. I think I'm doing  essentially the same thing that
you are doing above. Hmmmm.

I already sent Ryan a war, but I've added explicit ids to all my containers,
so I will send  him an updated war and cc you.

Thanks for all your help, I really appreciate it.


david

>
>
> Andy
>
>
> David Geary wrote On 5/25/2009 1:36 PM ET:
>
> 2009/5/24 Jim Driscoll <Jim.Driscoll at sun.com>
>
> On 5/24/09 8:39 PM, David Geary wrote:
>
> And that id is *exactly* what <f:ajax> says it cannot find. It's in the
> page, but <f:ajax> cannot find it?!?
>
> My guess is that <f:ajax> is evaluating the id before the page is
> completely constructed, and therefore, it doesn't find it, but that's
> just a WAG on my part.
>
> So, if this is not correct:
>
> <f:ajax render="#{cc.parent.clientId}:image"/>
>
> Then how do I access the image in the parent (map) component?
>
> It seems to me that I'm using <f:ajax> correctly, but I'd be happy to be
> told otherwise.
>
>
>
>  It's late, but yes, that looks correct, and yes, your guess sounds
> correct.  Could you file this as an impl bug?
>
>
> Okay. I'll send a WAR to Ryan, and see if we can get this fixed asap. I'd
> like to get this working for my J1 demo.
>
>
>
> Now, we do need to fix that as a bug, but I must argue that your use case
> represents bad practice.  Your component is using ajax inside a component to
> modify the using page.  That's difficult to maintain, and I suspect it will
> lead to obfuscated code quite easily.  I'd instead argue that if you are
> going to have one component modify another, you should either group them
> into one composite, or, have the ID of the component you act upon passed in
> as an attribute (say "for="?)
>
>
> Yup, I concur--too much coupling between parent and child components.
> Unfortunately, I can't seem to pass the component ID to the child component
> either. IOW, this doesn't work:
>
> In parent component:
>
>   <places:mapZoomControl componentToRender="#{cc.clientId}:image"/>
>
> And in mapZoomControl.xhtml:
>
>   <composite:attribute name="componentToRender"/>
>   ...
>   <f:ajax render="#{cc.attrs.componentToRender}"/>
>
> I suppose it's not surprising that the preceeding doesn't work--it looks
> like a manifestation of the same bug. I'll give Ryan a buzz.
>
> Thanks,
>
>
> david
>
>
>
> Of course, that second usage suggestion opens up another hole in the cc api
> - the lack of ability to do name resolution.  Though if/when we get method
> parameters, then cc.findComponent will work, I guess.    Though I've been
> saying that for a pretty long time now, I should check on when we're getting
> those method params...
>
> Jim
>
>
>
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: http://lists.jboss.org/pipermail/jsr-314-open-mirror/attachments/20090525/27f8e9e6/attachment.html 


More information about the jsr-314-open-mirror mailing list