david.shapiro has asked for the wisdom of the Perl Monks concerning the following question:

Hello, I have a perl cgi.pm page I made that I now want to have it display a message that something is loading and then have that message go away when it has completed without reload/refresh. I loaded CGI::Ajax, but I have no clue on how to make an CGI::Ajax page. I see that the create a $pjx object and export perl function. They then run a $pjx->build_html function in which they pass in the $cgi object and a function name to make the html page (\&Show_HTML). In my code I have it calling $cgi->header and $cgi->start_html with things I want (i.e., the style sheet to use and location of a javascript file (main.js). If I use $pjx->build_html, how do I specify my css and js files? Lastly, if you clear that up, how do I do my initial requirement? Does anybody have a short example of how to display text in the manner I need (i.e., I have an Update button that once it is clicked with display loading... that will then run a function to load stuff, and then I need the message to save complete for a few seconds and then disappear)? By the way, do I need $cgi->end_html anymore? Do I find out param values with $cgi->param, or do I use the pjx object to do this? It is confusing on how to move from CGI.pm to CGI::Ajax.

Replies are listed 'Best First'.
Re: Simple CGI::Ajax
by scorpio17 (Canon) on Jul 25, 2007 at 18:32 UTC

    Here's an example to get you started.

    First, put this file where ever your HTML files like to live. I called it it ajaxdemo.htm:

    <html> <head> <title>Ajax Demo</title> <script type="text/javascript"> function createRequestObject() { var req; if (window.XMLHttpRequest) { // Firefox, Safari, Opera... req = new XMLHttpRequest(); // alert('Detected a modern browser - very good!'); } else if (window.ActiveXObject) { // Internet Explorer 5+ req = new ActiveXObject("Microsoft.XMLHTTP"); // alert('Detected an old IE browser'); } else { // error creating the request object, // (maybe an old browser is being used?) alert('There was a problem creating the XMLHttpRequest object'); req = ''; } return req; } // Make the XMLHttpRequest object var http = createRequestObject(); function sendRequest() { var now = new Date(); http.open('get', 'http://www.myhostname.com/cgi-bin/myscript.pl?noca +che='+now.getTime()); http.onreadystatechange = handleResponse; http.send(null); } function handleResponse() { if(http.readyState == 4 && http.status == 200){ var response = http.responseText; // Text returned FROM perl scrip +t if(response) { // UPDATE ajaxTest content document.getElementById("ajaxstuff").innerHTML = response; setTimeout('wipeout()', 2000); // wait 2 seconds, then clear mes +sage } } } function wipeout() { document.getElementById("ajaxstuff").innerHTML = ''; x = document.getElementById("mybutton"); x.disabled = false; } function doclick() { x = document.getElementById("mybutton"); x.disabled = true; document.getElementById("ajaxstuff").innerHTML = 'working...'; sendRequest(); return true; } </script> </head> <body> <p>Click here to begin:</p> <form><input type="button" id="mybutton" onClick="doclick()" value="Go +!"></form> <hr/> <p><em><div id="ajaxstuff" > &nbsp; </div></em></p> <hr/> </body> </html>

    Note that there is a lot of javascript in here. You could put it in a file of its own if you prefer. The main part that makes this 'AJAX' is the createRequestObject() function, which uses the XMLHttpRequest object. This is the thing that lets the client (browser) and server pass messages back and forth asynchronously. You have to pull a few tricks to keep old browsers happy, unfortunately.

    Down near the end of the file, there is a DIV tag with id="ajaxstuff". Remember this, because we'll see it again later.

    There's also a button with an a onClick event defined. When you click it, the javascript doclick() function gets called. It disables the button (so someone can't click it again while it's busy), changes the DIV contents to "working..." (so you know it's doing something), then calls sendRequest().

    sendRequest() sends a request to the server, asking it to run myscript.pl. All this script does is sleep for 5 seconds then print "Done!":

    #!/usr/bin/perl use strict; sleep 5; print "content-type: text/html\n\n"; print "Done!\n";

    If this were a real script (and not a demo) it would actually do something useful. Note that you could put the same URL into your browser's address bar. It should print "Done!" after 5 secods. There's nothing special about it, it's just a regular cgi script. You should put it where ever your CGI scripts like to live. You'll have to edit the URL in the example to match your server, etc.

    Note that I use the javascript getTime() function to assign a value to the 'nocache' variable. getTime returns the integer number of seconds since the beginning of time, so it should be different every time you call it. The script doesn't actually use the nocache variable, but it makes each URL unique, which prevents the browser from caching it. If your browser caches the URLs, then not all of your requests will actually be sent to the server. This can create really hard to debug problems.

    If you needed to send data to the server, you'd do it the same way (string the name=value pairs together on the URL), and the server side script would process them the same as any other CGI script.

    After sendRequest() askes the server to run this script, it then defines a response handler, the function handleResponse(). When the server responds to the request the handleResponse() function will be called. The client has no way of knowing if/when the server will respond. Think of it like an interrupt, or an event to be processed, like a mouse click.

    For this demo, the response handler grabs the output of the script (the message "Done!", remember?) and stuffs it into the DIV (overwriting the "working..." message).

    I went one step further and used the setTimeout function next. In this example, it sets a 2 second timer (time value given in milliseconds), then calls the wipeout() function. wipeout() erases the 'Done!" message (overwrites the old contents with the empty string), then re-enables the button, so you can click it again.

    Okay - I never used CGI::Ajax anywhere. I'm not a big fan of that module. For what you're trying to do, I think it would be overkill. If you ever need to pass several input values from your web page to the server, then get several result values back, and stick each of them into different DIVS, then it does help automate generation of most of the javascript necessary to tie everything together.

    I prefer to concatenate everything together into one big string on the client end, pass it to the server, parse it (perl makes this easy), and then send back one big string. Javascript has a 'split' function, too. If you need more structured data, you can use JSON (it's still basically one big string, and there are perl modules for reading/writing it). But that's just me...

    I hope this helps get you started. Good luck!

      You are a God amongst men! Thank you for such a complete demo! I will work out how it all works and see if I can apply it to my program.
    Re: Simple CGI::Ajax
    by wazoox (Prior) on Jul 25, 2007 at 14:47 UTC
        Sorry it is unclear. I have something kind of working,but not really working. When I click on the Update button, I see my post appear on the screen (ie., healthchk.cgi?fname=displayText&args=Update&update=Update) for a second and then it flashes off the screen instead of the text I was expecting from the &displayText function. Here is part of the code:
        #!/usr/opt/perl5/bin/perl -Tw use CGI; use CGI::Carp qw(warningsToBrowser fatalsToBrowser); use CGI::Ajax; use strict; use warnings; use English; use Env qw(PATH IFS CDPATH EMV BASH_EMV); $ENV{PATH} = "/bin:/usr/bin:/usr/local/AppAdmin/healthchk/bin:/usr/loc +al/healthchk/bin"; delete @ENV{ 'IFS', 'CDPATH', 'ENV', 'BASH_ENV' }; my $home = "/usr/local/AppAdmin/healthchk"; my $html = ""; my $cgi = CGI->new(); my $ajax = CGI::Ajax->new(displayText => \&displayText ); $ajax->JSDEBUG(1); print $ajax->build_html($cgi,\&main); sub main() { $html = <<HTML; <html> <head> <title>Siebel HealthCheck</title> <LINK href="/healthchk/style.css" rel="stylesh +eet" type="text/css"> </head> <body> <table class="banner" border="0" cellspacing="0" cellp +adding="0" bgcolor="#000000"> <tr valign="top"> <td class="banner" width="10%"> <img src="/healthchk/images/bc +bsnc.gif" width="100%" > </td> <td class="banner" width="80%"> &nbsp; </td> <td class="banner" width="10%"> <font align="right" size="1px" + color=white>&#169; 2007 David Shapiro</p></font> </td> </tr> </table> <h1>Siebel HealthCheck</h1> HTML # Do something print_response() if $cgi->param; my $url = $cgi->url(-relative => 1); $html .= <<HTML; <form action="$url" method="post"> <center> Environment&nbsp; <input type="checkbox" class="cbox" name="envi +ronment" value="ALL" >&nbsp;ALL&nbsp;&nbsp; <input type="checkbox" class="cbox" name="envi +ronment" value="prod">&nbsp;prod&nbsp;&nbsp; <input type="checkbox" class="cbox" name="envi +ronment" value="pstage">&nbsp;pstage&nbsp;&nbsp; <input type="checkbox" class="cbox" name="envi +ronment" value="qa">&nbsp;qa&nbsp;&nbsp; <input type="checkbox" class="cbox" name="envi +ronment" value="dev">&nbsp;dev&nbsp;&nbsp; <input type="checkbox" class="cbox" name="envi +ronment" value="pmt">&nbsp;pmt&nbsp;&nbsp; <input type="checkbox" class="cbox" name="envi +ronment" value="sand">&nbsp;sand&nbsp;&nbsp; <br><br><input type="submit" value="Submit" cl +ass="btn"> <input type="submit" id="update" onClick="disp +layText(['update'],['displayDiv']);" value="Update" class="btn"><br>< +br> </center> </form> <div id="displayDiv"></div> </body> </html> HTML return $html; } sub displayText() { my $text = shift; $html .= <<HTML; <h2>Updating status...</h2> HTML return; }
        It looks like you're not sending any headers. I renew my first suggestion : get the script simply working first, then "ajaxify" it. I advise you to use Firefox plus Live HTTP headers to see what's coming from and to your browser, too.