I meant to send this to the open mailing list in the first place, so I'm forwarding my original message and Ken's response.

---------- Forwarded message ----------
From: Ken Paulsen <Ken.Paulsen@sun.com>
Date: 2009/11/16
Subject: Re: [jsr-314-eg] Ajax bugs?
To: jsr-314-eg@jcp.org
Cc: Cay Horstmann <cay@horstmann.com>



Below are some of my experiences... may be helpful, may not be. :)

Ken


David Geary wrote:
Sorry for the long email, but it should be easy to grok.

I'm trying to get a simple Ajax polling example working, and I'm not having much luck. I think I'm running across two Ajax bugs in mojarra. Here's my markup:

          <h:form prependId="false">

            <h:commandButton value="Start polling" id="startPollingButton"
                           onclick="startPolling(this, event, 'pollOutput')"
                            action="#{user.incrementPollIndex}">

              <f:ajax listener="#{user.ajaxListener}"/>

            </h:commandButton>
         
            <br/>
           
            <h:outputText value="pollOutput is: #{user.pollIndex}" id="pollOutput"/>

            <br/>

            <h:commandButton id="stopPollingButton" value="Stop polling" action="#{user.stopPolling}"/>

          </h:form>

I have two buttons, one to start polling, and the other to stop. I also have an output that displays a backing bean property (#{user.pollIndex}). When you click the start button, it calls my startPolling JavaScript function. That function starts a timer which repeatedly calls jsf.ajax.request():

      <script type="text/javascript">
        function startPolling(component, event, renderComponents) {
           var pollFunction = function poll() {
                jsf.ajax.request(component.id, event, { render: renderComponents } )
            }
            var timer = window.setInterval(pollFunction, 200);
        }
      </script>

Bug #1: Ajax listener is not invoked when I call jsf.ajax.request() manually.
I ran into the same issue when trying to do something similar recently.  This should make what you have work:

    function startPolling(component, event, renderComponents) {
        var props = {
            execute: component.id;
            render: renderComponents;
            }
        props[component.id] = component.id;
        var pollFunction = function poll() {
            jsf.ajax.request(component, event, props);
        }
        window.setInterval(pollFunction, 200);
    }
   
I am not sure if the component.id = component.id in "props" is necessary, it is for the button component that I am using and I haven't spent the time to find out why or if it is also needed for h:* components.  I think the execute one is needed also.  You can also use '@all' to execute everything (or render everything).  Also, it's better to pass in 'component' instead of 'component.id' for the src argument since the implementation just searches for 'component' again if you don't pass it in directly.

One thing you should be aware of is that if your requests do not complete in 2 seconds, they'll start queuing up.  There should be a way to terminate previously queued requests that have not yet fired... but I don't think there is a public way to do this.  Please correct me if I'm wrong.



Everytime I call jsf.ajax.request() in poll(), the button's action gets called, as I expect. However, the Ajax listener associated with the button is only called when I actually click the button, and not when the poll() function is invoked. I expect the Ajax listener, like the button's action, to be invoked everytime I call jsf.ajax.request(). That seems like a reasonable expectation to me.
I think this is expected b/c you are not "executing" the button in your Ajax request.  Hmm... after looking at the jsf.js code, it does default to component.id (and component.name for some reason).  So it should work as you expect... perhaps I worked around this bug when I shouldn't have had to... I'm confused now. :)


After rummaging around in the RI code, I found that I could get mojarra to call the Ajax listener everytime I call poll() if I change the call above to jsf.ajax.request() to this:

             jsf.ajax.request(component.id, event, { render: renderComponents, 'javax.faces.behavior.event': 'action' } )

Ugh. That's not very intuitive. Why do I have to do that? I expect the Ajax listener to be called without having to resort to such slight of hand.


Bug #2: Removing components from the tree in an Ajax listener does not remove the component from the page when the Ajax call returns.

When polling is complete, I want to remove the output, so in the Ajax listener I tried two things: 1. set the rendered attribute of the output to false. 2. remove the output component from it's parent's list of components. Neither works. In both cases, the output remains in the page until I do a full page refresh, and then it disappears. The output no longer updates after it's removed (or it's rendered attribute is set to false), but it's still in the page. Note that I am specifying that the output component is rendered after the Ajax call.

If I manually remove a component, or set it's rendered attribute to true, during an Ajax call, I expect that component to disappear when the Ajax call returns, provided of course, that it's one of the components to be rendered when the Ajax call returns.
I ran into this also.  However, if you think about it... this one makes sense.  You tell it to go through the render phase for a component that says don't produce any output... so that's what it does (render==false case).  And for the render == invalid id case, the current behavior does nothing.  However, it probably should be smarter and return a "delete" xml element to handle this...

To work-a-round this one, we added a hack that detected an empty Ajax response and manipulated the HTML DOM in our own JS... fortunately we only expected 1 object to be upated so this worked for us.  Not a good work-a-round.  We possibly could have changed our server-side logic to change the 'style' of the component to 'display:none;' instead... anyway, I agree with you on this issue.

Ken


Are these bugs, or are my expectations out of line?

Thanks,


david