http://www.perlmonks.org?node_id=640039

hacker has asked for the wisdom of the Perl Monks concerning the following question:

I've been working on a web form that allows a user to fill in some parameters like url, link depth, images parameters and such.

When they submit the form, I currently do the following:

  1. Fetch the URL and extract all of the links found on the main page, returning a count
  2. Convert the content into another non-HTML format
  3. Display a configuration stanza so the user can use it to reproduce the same action on their own local machine at a later time,
  4. Present the user with the non-HTML converted file format for download ("Save As...")
  5. etc..

What I'd like to try to do, is make the form more usable for users, without the need to issue a Submit to return much of the basic results.

I'm not looking for "javascript form valdiation", but something to actually perform an action on the server-side every time the user moves between form fields.

For example, the user enters a URL into a textfield, and as they tab to the next form element, I'm fetching the URL they've entered and returning the count of links found, as they continue to fill out the rest of the form and before they submit the form.

Enter AJAX..

Have any monks implemented AJAX form validation and XMLHttpRequest style objects and server calls with all of the form and backend processing handled via Perl itself?

I'm looking for gotchas, pointers, samples and other things I can use to learn how to do this.

Barring that, is there another approach to making the form more usable for everybody's grandmother, without using AJAX or client-side validation tricks?

Thanks!

  • Comment on AJAX'ifying a web form and dynamic server validation through Perl

Replies are listed 'Best First'.
Re: AJAX'ifying a web form and dynamic server validation through Perl
by erroneousBollock (Curate) on Sep 20, 2007 at 06:30 UTC
    I'm looking for gotchas, pointers, samples and other things I can use to learn how to do this.
    Some things I can recommend from experience:
    • don't do the XmlHttpRequest's yourself (use a mature, robust, cross-platform wrapper. eg: Dojo.io)
    • don't bother with REST, you'll depend too much on the webserver accepting non-"standard" request types.
    • do use JSON or SOAP (I'd say JSON as most browser SOAP implementations are not well done)
    • do perform request validation on both the client and server side (on the client to help the user, on the server for security)
    • do make sure to implement some form of sequencing in your session-management so that service replies aren't handled out-of-order by the client (if that's a problem)
    • do handle exceptions on the server side gracefully, all client-side submission Javascript should be ready to handle an error condition (not related to HTTP)
    • don't use synchronous AJAX calls under any circumstances as they have extremely poor robustness in most browser implementations.
    • do make sure to use a tool like FireBug in order to debug (and profile) you're AJAX requests.
    • do show the user some useful indication that the server is doing something; robustly handle error conditions so that the user-interface never hangs.
    • if the user shouldn't double-click (or some other form field interaction), make sure they can't.
    • if possible, detect old/phone-based browsers and redirect them to an "old school" form on a different page.

    -David.

Re: AJAX'ifying a web form and dynamic server validation through Perl
by Anonymous Monk on Sep 20, 2007 at 06:27 UTC
Re: AJAX'ifying a web form and dynamic server validation through Perl
by mattk (Pilgrim) on Sep 20, 2007 at 12:15 UTC
    I recently started using ExtJS, with CGI::Application and JSON. It works nicely, and abstracts away a lot of the pain of cross-browser compatibility, though the large file size can be a problem (there are other, lighter toolkits that may better serve your purpose). Here's a 30 second example that gets you AJAX style updates:
    callbackFunction = function(options, success, response) { if (success === true) { var data = Ext.decode(response.responseText); doSomething(data); // or Ext.get('contents-div').dom.innerHTML = response.responseText; } else { Ext.MessageBox.alert('Error', 'Something blew up.'); } } Ext.Ajax.request({ url: '/path/to/cgi', callback: callbackFunction, params: { rm: 'ajax_function' } });
    The Perl part, assuming you've tagged this sub as a runmode:
    sub ajax_function { my ($self) = @_; my $data = doStuff(); # or you could return HTML return $self->{json}->objToJson($data); }
    It has other cool stuff built in, like the ability to automatically generate fields and labels for a form, and bind the validation and submission to callback functions, visual effects, data stores and interfaces for all different kinds of sources... it actually got me interested in learning JS. Pretty cool stuff.
Re: AJAX'ifying a web form and dynamic server validation through Perl
by scorpio17 (Canon) on Sep 20, 2007 at 14:43 UTC
    Here's a quick demo that does something like you want. The HTML page contains a form with 4 fields: only two of these feature ajax-enabled verification. An XMLHttpRequest object is created for each field, since each will be verified independently (most examples I have found online show a single object making a single request - often sending back multiple results which update multiple parts of the page. Here, each object sends a request for one form element and sends back a single result.) The onChange event is triggered when you enter a value then tab to the next field. For this example, I have one script to verify the "word" value, and another script to verify the "number" value. You could, of course, combine these together, and have one request call a single script to validate both, and send back results for each, etc.

    verify.htm

    <html> <head> <META HTTP-EQUIV="Expires" CONTENT="Tue, 04 Dec 1993 21:29:02 GMT"> <title>Ajax Form Verify</title> <script type="text/javascript"> function createRequestObject() { var req; if (window.XMLHttpRequest) { // Firefox, Safari, Opera... req = new XMLHttpRequest(); } else if (window.ActiveXObject) { // Internet Explorer 5+ req = new ActiveXObject("Microsoft.XMLHTTP"); } 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_number = createRequestObject(); var http_word = createRequestObject(); function verifyNumberRequest() { var number = document.getElementById("number").value; if ( number ) { var url = 'http://www.YOURHOSTHERE.com/cgi-bin/verify_number.pl?nu +mber='+number; http_number.open('get', url ); http_number.onreadystatechange = handleNumberResponse; http_number.send(null); } } function verifyWordRequest() { var word = document.getElementById("word").value; if ( word ) { var url = 'http://www.YOURHOSTHERE.com/cgi-bin/verify_word.pl?word +='+word; http_word.open('get', url ); http_word.onreadystatechange = handleWordResponse; http_word.send(null); } } function handleNumberResponse() { if(http_number.readyState == 4 && http_number.status == 200){ var response = http_number.responseText; // Text returned FROM per +l script if(response) { // UPDATE ajaxTest content document.getElementById("number_verify_result").innerHTML = resp +onse; } } } function handleWordResponse() { if(http_word.readyState == 4 && http_word.status == 200){ var response = http_word.responseText; // Text returned FROM perl +script if(response) { // UPDATE ajaxTest content document.getElementById("word_verify_result").innerHTML = respon +se; } } } </script> </head> <body> <h1>Ajax Form Verify Demo</h1> <form> <table> <tr> <td> Enter color: </td> <td> <input type="text" name="color"> </td> <td> &nbsp; </td> </tr> <tr> <td> Enter 4 digit number: </td> <td> <input type="text" name="number" id="number" onchange="verifyNumb +erRequest();return true;"></td> <td> <div id="number_verify_result"> &nbsp; </div> </td> </tr> <tr> <td> Enter 4 letter word: </td> <td><input type="text" name="word" id="word" onchange="verifyWordReque +st();return true;"></td> <td> <div id="word_verify_result"> &nbsp; </div> </td> </tr> <tr> <td> Enter name: </td> <td> <input type="text" name="name"> </td> <td> &nbsp; </td> </tr> </table> </form> <hr/> </body> </html>

    verify_number.pl

    #!/usr/bin/perl # script to verify that value is a 4 digit number use strict; use CGI qw(:all); $|=1; # no I/O buffering my $number = param("number") || ''; print "content-type: text/html\n\n"; if ( $number =~ /\b\d{4}\b/ ) { print "<p style=\"font-family: courier; color: green\"><-- OK</p>\n" +; } else { print "<p style=\"font-family: courier; color: red\"><-- ERROR: not +a 4 digit number!</p>\n"; }

    verify_word.pl

    #!/usr/bin/perl # script to verify that value is a 4 letter word use strict; use CGI qw(:all); $|=1; # no I/O buffering my $word = param("word") || ''; print "content-type: text/html\n\n"; if ( $word =~ /\b\w{4}\b/ ) { print "<p style=\"font-family: courier; color: green\"><-- OK</p>\n" +; } else { print "<p style=\"font-family: courier; color: red\"><-- ERROR: not +a 4 letter word!</p>\n"; }