2

The intent is to display a wait spinner icon while the server is preparing the file for download. In my use case I am generating a large report, this takes some time server-side before the browser's Download / Save As prompt gets invoked. I can show the wait spinner no problem, but am having trouble finding a way to clear it.

Current relevant code:

<h:commandButton id="generate" value="Generate Report" type="submit" 
    action="#{bean.generateReport()}" 
    onclick="#{rich:component('waitIcon')}.start();"/>

<a4j:status id="waitIcon">
     <f:facet name="start">
       <h:graphicImage value="/images/ai.gif" alt="ai" />
    </f:facet>
</a4j:status>

Where bean.generateReport() is an action that takes 1-10secs serverside, then spits back a download response.

This is using richfaces a4j:status indicator, as it gives useful API like .start() and .stop() but it could just as well be any dom element and setting visibility. The problem is I can't get the right event to hook on. I need to capture something like onresponsereceived...

Solutions I've tried:

Using a4j:commandButton offers an oncomplete event, but the button generates an AJAX request, which is incapable of starting the download. (See h:command button oncomplete action)

Using a window.timeout function to call .stop() on the component. This works on a functional level, but since the generation time swings pretty wildly between 1-10 secs, it makes the indicator kind of not reflecting reality.

Anyone have any good ideas on handling this?

Community
  • 1
  • 1
Eric
  • 1,953
  • 4
  • 24
  • 33
  • Your question title is confusing. You seem rather to be interested in firing JS code when file download is finished. Can you please revise this? – BalusC Jun 15 '13 at 19:43
  • Im actually interested in firing JS code when the file download starts (Response received by browser). The event delta I am looking to display the spinner is from Request Sent -> Response Received. Sent is easy with onclick event. Received... not so :) – Eric Jun 17 '13 at 15:40

3 Answers3

1

One way would be to return to the same page wherein you conditionally render a piece of JS code which initiates the actual download.

<h:form>
    ...
    <h:commandButton value="submit" action="#{bean.prepareFileForDownload}" />
    <h:outputScript rendered="#{bean.fileReadyForDownload}">
        // Do here your thing to indicate start of download.
        window.location = '#{bean.fileDownloadURL}';
    </h:outputScript>
</h:form>

Another way would be to check for a cookie at intervals. You only need to generate an URL safe value as some kind of download token (System.currentTimeMillis() is perfectly applicable for this).

<h:form>
    ...
    <input type="hidden" name="token" value="#{bean.token}" />
    <h:commandButton value="submit" action="#{bean.downloadFile}" onclick="checkToken(this.form.token)" />
    <h:outputScript>
        function checkToken(token) {
            var pollDownloadStart = setInterval(function() {
                if (document.cookie.indexOf("download=" + token) > -1) {
                    document.cookie = "download=" + token + "; expires=" + new Date(0).toGMTString() + "; path=/";
                    clearInterval(pollDownloadStart);
                    // Do here your thing to indicate start of download.
                }
            }, 500);
         }
    </h:outputScript>
</h:form>

with this in downloadFile()

// Prepare download here.
// ...

// Once finished, set cookie and stream download to response.
Cookie cookie = new Cookie("download", token);
cookie.setPath("/");
response.addCookie(cookie);
// ...
BalusC
  • 1,082,665
  • 372
  • 3,610
  • 3,555
0

The simplistic solution would be to set the setTimeout to say 1 second to check whether the response has been received. The setTimeout will repeat until cleared.

So you could do:

var responseReceived, hTimer = setTimeout( function() {
    if( responseReceived ) {
        // code to stop/hide spinner here
        clearTimeout( hTimer );
    }
}, 1000 );

// responseReceived is set when the response is received

Alternatively, what you need is what is called a comet in programming terms. For your needs a Ajax long-poll should suite your needs best. You basically open a ajax request to the server (which is asynchronously) and the callback function does whatever you want when the server responds. Take a look at these references:

Community
  • 1
  • 1
Precastic
  • 3,742
  • 1
  • 24
  • 29
  • The getting ahold of when `responseReceived` is the real problem :) The iframe suggestion in that comet link looks to be the most promising, however I believe I will have similar troubles trying to initiate a download via javascript/ajax through that iframe. Will prove it out and reply... I wish there was a more direct solution. – Eric Jun 14 '13 at 20:39
  • Ah, I misunderstood your point about window.timeout. In that case the comet solution is probably your best bet – Precastic Jun 14 '13 at 21:04
0

If I understand correctly, your user fills some data in a form then clicks the submit button to POST the data to the server which then processes it and after a while it generates a response which triggers the "Save as..." dialog window. If that is correct, the solution is to create a hidden iframe, then redirect the server's response to that iframe by setting the target property on the form, and finally hooking on the onload event of the iframe.