views:

297

answers:

4

I've wasted at least a half day of my company's time searching the Internet for an answer and I'm getting wrapped around the axle here. I can't figure out the difference between all the different technology choices (long polling, ajax streaming, comet, XMPP, etc.) and I can't get a simple hello world example working on my PC.

I am running Apache 2.2 and ActivePerl 5.10.0. JavaScript is completely acceptable for this solution. All I want to do is write a simple Perl CGI script that when accessed, it immediately returns some HTML that tells the user to wait or maybe sends an animated GIF. Then without any user intervention (no mouse clicks or anything) I want the CGI script to at some time later replace the wait message or the animated GIF with the actual results from their query.

I know this is simple stuff and websites do it all the time using JavaScript, but I can't find a single working example that I can cut and paste onto my machine that will work in Perl.

Here is my simple Hello World example that I've compiled from various Internet sources, but it doesn't seem to work. When I refresh this Perl CGI script in my web browser it prints nothing for 5 seconds, then it prints the PLEASE BE PATIENT web page, but not the results web page. So the Ajax XMLHttpRequest stuff obviously isn't working right. What am I doing wrong?

#!C:\Perl\bin\perl.exe

use CGI; 
use CGI::Carp qw/fatalsToBrowser warningsToBrowser/; 

sub Create_HTML {
    my $html = <<EOHTML;
<html>
<head>
  <meta http-equiv="pragma" content="no-cache" />
  <meta http-equiv="expires" content="-1" />
  <script type="text/javascript" >

var xmlhttp=false;
/*@cc_on @*/
/*@if (@_jscript_version >= 5)
// JScript gives us Conditional compilation, we can cope with old IE versions.
// and security blocked creation of the objects.
 try {
  xmlhttp = new ActiveXObject("Msxml2.XMLHTTP");
 } catch (e) {
  try {
   xmlhttp = new ActiveXObject("Microsoft.XMLHTTP");
  } catch (E) {
   xmlhttp = false;
  }
 }
@end @*/
if (!xmlhttp && typeof XMLHttpRequest!='undefined') {
    try {
        xmlhttp = new XMLHttpRequest();
    } catch (e) {
        xmlhttp=false;
    }
}
if (!xmlhttp && window.createRequest) {
    try {
        xmlhttp = window.createRequest();
    } catch (e) {
        xmlhttp=false;
    }
}

  </script>

  <title>Ajax Streaming Connection Demo</title>
</head>
<body>

  Some header text.
  <p>
  <div id="response">PLEASE BE PATIENT</div>
  <p>
  Some footer text.

</body>
</html>

EOHTML
    return $html;
  }

my $cgi = new CGI;
print $cgi->header;
print Create_HTML();

sleep(5);
print "<script type=\"text/javascript\">\n";
print "\$('response').innerHTML = 'Here are your results!';\n";
print "</script>\n";
+3  A: 

If your process relies on query-string parameters, a simple meta-refresh would suffice. E.g. if they load http://yoursite.com/message?foo=1, then that can output a meta tag like:

<meta http-equiv="refresh" content="0; http://yoursite.com/realquery?foo=1" />

And some HTML that has your "please wait" message. The realquery script would actually execute the query and the HTML output by message will remain on the screen until realquery provides some output.

If the query relies on POST data, then it gets a little more complicated, because you can't redirect a POST. You can, however, output a form with some hidden fields and use Javascript to submit it. For example:

<script type="text/javascript">
    window.onload = function() { 
        document.getElementById( 'form_with_hidden_fields' ).submit();
    }
</script>

<form method="POST" action="realquery" id="form_with_hidden_fields">
    <input type="hidden" name="foo" value="1" />
    ...
</form>

Please wait while your query is processed...

If you're interested in an AJAX solution, here's an example using jQuery:

$( '#submit-button' ).click( function() { 
    // show a "please wait" image
    $( '#status-div' ).html( '<img src="please_wait.gif" />' );  // animated gif

    // get form values
    var formdata = { foo: $( 'input#foo' ).val(),
                     ...
                   };

    // submit form via ajax:
    $.ajax( { type: "POST", url: "/realquery", data: formdata, success: function() { 
        $( '#status-div' ).html( '<img src="success.gif" />' );
    } );
} );

And you could attach that to a form like:

<form>
    <input type="text" name="foo" id="foo" />
    <input type="submit" id="submit-button" />
    <div id="status-div"> </div>
</form>

The empty status-div div will receive an image tag that points to a "please wait" image (this can be an animated gif). When the Ajax query finishes, it's replaced by a "success" image.

friedo
Okay, that worked. Thanks for the solution! I will use this solution if nobody answers with an Ajax solution. The benefit would be refreshing just the results section of the web page without sending and refreshing the whole web page on the browser.
Kurt W. Leucht
Another downside to the meta redirect solution is that it muddles up the URL with the query string in the web browser, where I believe the Ajax solution doesn't actually change the URL when it updates sections of your web page.
Kurt W. Leucht
I added an Ajax example using jQuery.
friedo
I tried your jquery example and cannot get it to work. Could you please post a complete working example rather than short snippets. No need to use animated GIFs ... just put some text there. I'm not sure I'm putting your code snippets in the right location and using them properly. I've never used Ajax nor Jquery before.
Kurt W. Leucht
+2  A: 

See Watching long processes through CGI by Randal Schwartz.

Sinan Ünür
This article is immensely helpful!
justkt
+1  A: 

Here is a complete working example using friedo's HTTP meta refresh solution. This is not my personal first choice solution because it modifies the URL in the browser and it also refreshes the whole web page.

#!C:\Perl\bin\perl.exe
use CGI; 
use CGI::Carp qw/fatalsToBrowser warningsToBrowser/; 
sub html_page {
    my ( $meta_string, $results_string ) = @_;
    my $html = <<EOHTML;
<html>
<head>
  $meta_string
  <title>Two Stage Web Page Demo</title>
</head>
<body>
  Some header text.
  <p>
  $results_string
  <p>
  Some footer text.
</body>
</html>
EOHTML
    return $html;
  }

my $cgi = new CGI;
print $cgi->header;
if ($cgi->param()) {
    if ($cgi->param('doResults') eq "true") {
        sleep(5);
        print html_page('', 'Here are your results!');
    }
}
else {
    my $meta_refresh = '<meta http-equiv="refresh" content="0; /cgi-bin/twoStageScript.pl?doResults=true" />';
    print html_page($meta_refresh, 'PLEASE BE PATIENT');
}
exit;
Kurt W. Leucht
A: 

Finally got an Ajax version working. The slow.pl file is the file that takes a while to return.

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd"&gt;
<html>
    <head>
        <title>Two Stage web page demo using Ajax</title>
    </head>
    <body>
        <h1>Two Stage web page demo using Ajax</h1>
        <div id="result">
            Users input form goes here.
            <p>
            <input type="submit" value="Here is your submit button" id="load_basic" />
        </div>
        <script type="text/javascript" src="jquery-1.4.2.js"></script>
        <script type="text/javascript">
        $.ajaxSetup ({
            cache: false
        });
        var ajax_load = "Please be patient, this could take a while. <p> <img src='img/load.gif'/>";

        //  load() function
        $("#load_basic").click(function(){
            $("#result").html(ajax_load).load("/cgi-bin/slow.pl");
        });
        </script>
    </body>
</html>
Kurt W. Leucht