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.<br><br><div class="gmail_quote">---------- Forwarded message ----------<br>From: <b class="gmail_sendername">Ken Paulsen</b> <span dir="ltr"><<a href="mailto:Ken.Paulsen@sun.com">Ken.Paulsen@sun.com</a>></span><br>
Date: 2009/11/16<br>Subject: Re: [jsr-314-eg] Ajax bugs?<br>To: <a href="mailto:jsr-314-eg@jcp.org">jsr-314-eg@jcp.org</a><br>Cc: Cay Horstmann <<a href="mailto:cay@horstmann.com">cay@horstmann.com</a>><br><br><br>
<div bgcolor="#ffffff" text="#000000">
<br>
Below are some of my experiences... may be helpful, may not be. :)<br>
<br>
Ken<div class="im"><br>
<br>
David Geary wrote:
<blockquote type="cite">Sorry for the long email, but it should be easy to grok.<br>
<br>
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:<br>
<br>
<h:form prependId="false"><br>
<br>
<h:commandButton value="Start polling"
id="startPollingButton"<br>
onclick="startPolling(this, event,
'pollOutput')" <br>
action="#{user.incrementPollIndex}"><br>
<br>
<f:ajax listener="#{user.ajaxListener}"/><br>
<br>
</h:commandButton><br>
<br>
<br/><br>
<br>
<h:outputText value="pollOutput is: #{user.pollIndex}"
id="pollOutput"/><br>
<br>
<br/><br>
<br>
<h:commandButton id="stopPollingButton" value="Stop
polling" action="#{user.stopPolling}"/><br>
<br>
</h:form><br>
<br>
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():<br>
<br>
<script type="text/javascript"><br>
function startPolling(component, event, renderComponents) {<br>
var pollFunction = function poll() {<br>
jsf.ajax.request(<a href="http://component.id" target="_blank">component.id</a>, event, { render:
renderComponents } )<br>
}<br>
var timer = window.setInterval(pollFunction, 200);<br>
}<br>
</script><br>
<br>
Bug #1: Ajax listener is not invoked when I call jsf.ajax.request()
manually.<br>
</blockquote></div>
I ran into the same issue when trying to do something similar
recently. This should make what you have work:<br>
<br>
<tt><div class="im"> function startPolling(component, event, renderComponents) {<br></div>
var props = {<br>
execute: <a href="http://component.id" target="_blank">component.id</a>;<br>
render: renderComponents;<br>
}<br>
props[<a href="http://component.id" target="_blank">component.id</a>] = <a href="http://component.id" target="_blank">component.id</a>;<br>
</tt><tt> var pollFunction = function poll() {<br>
jsf.ajax.request(component, event, props);<br>
}<br>
window.setInterval(pollFunction, 200);<br>
</tt><tt> }<br>
<br>
</tt>I am not sure if the <a href="http://component.id" target="_blank">component.id</a> = <a href="http://component.id" target="_blank">component.id</a> 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 '<a href="http://component.id" target="_blank">component.id</a>' for the src argument
since the implementation just searches for 'component' again if you
don't pass it in directly.<br>
<br>
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.<div class="im"><br>
<br>
<br>
<blockquote type="cite">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.<br>
</blockquote></div>
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 <a href="http://component.id" target="_blank">component.id</a> (and <a href="http://component.name" target="_blank">component.name</a> 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. :)<div class="im"><br>
<blockquote type="cite"><br>
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:<br>
<br>
jsf.ajax.request(<a href="http://component.id" target="_blank">component.id</a>, event, { render:
renderComponents, 'javax.faces.behavior.event': 'action' } )<br>
<br>
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.<br>
<br>
<br>
Bug #2: Removing components from the tree in an Ajax listener does not
remove the component from the page when the Ajax call returns.<br>
<br>
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.<br>
<br>
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.<br>
</blockquote></div>
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...<br>
<br>
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.<br><font color="#888888">
<br>
Ken</font><div class="im"><br>
<blockquote type="cite"><br>
Are these bugs, or are my expectations out of line?<br>
<br>
Thanks,<br>
<br>
<br>
david<br>
<br>
<br>
<br>
<br>
</blockquote>
</div></div>
</div><br>