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

I have thought myself into a box on this one. The background I have a web page that presents multiple forms to fill out. The fields are presented on the page based on Javascript. All of the logic and form checking is handled within Perl. The Javascript is only used for presentation.

I am trying to add a feature where if a Username is filled in the accompanying information will be looked up and automatically populated in other fields (Office location, phone number etc.)

I can't seem to figure out how to implement this within Perl. Javascript supports an onChange function that would allow me to trigger an action when the value changes for Username. But there isn't a method to call a Perl function from Javascript. And I haven't been able to find a Perl function that would trigger when the value in the Username field changes.

Thanks for any help.
Javahead Update Thanks everyone for the input. The web site is "another duty as assigned" so I'm just picking up web techniques. I was able to get the functionality I wanted with the CGI::Ajax module with only minimal changes. The security concerns are minimal. The webserver is internal only and the HR area requires a login and password.
  • Comment on CGI how to lookup a value when a Field is filled in

Replies are listed 'Best First'.
CGI/Ajax example: lookup a value when a field is filled in
by Your Mother (Archbishop) on Dec 06, 2008 at 02:24 UTC

    Here you go. It's minimal/simplistic but it's functional and self-contained. You should be able to walk through it and see how things are working. I doubt this thread will expand so I'm going to forgo the readmore tags.

    use strict; use warnings; use CGI qw(:standard); use Template; use JSON::XS; # Don't use CGI::Carp in production, it's here to make your life # easier while you experiment with this. use CGI::Carp qw(fatalsToBrowser); my $default = "splash"; my %actions = ( $default => \&show_form, ajax_lookup => \&ajax_lookup, ); my $action = param("x") || $default; my $executable = $actions{$action} || \&no_such_action; $executable->(); # Subroutines #--------------------------------------------------------------------- sub show_form { print header(); my $tt2 = Template->new(); $tt2->process(\*DATA, { self_uri => CGI::url(), usernames => [ map { $_->{username} } @{_du +mmy_db()} ], }) or die $tt2->error; } sub ajax_lookup { my $data = _dummy_db(); my $query = param("username"); my $result = {}; for my $row ( @{$data} ) { $result = $row if lc($query) eq lc($row->{username}); } print header("application/json"), encode_json($result); } sub no_such_action { print header(), h1("Nopers! Don't know how to do that!"); } sub _dummy_db { return [ { username => "paco", fish => "Sunfish", }, { username => "YourUncle", fish => "Coelacanth", }, { username => "hiragana", fish => "Monkfish", }, { username => "MosaicNL", fish => "Sixgill Shark", }, ]; } __DATA__ <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" lang="en-US" xml:lang="en-US"> <head> <title>OH, HAI! I CAN HAS AJAKS?</title> <script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.2.6/jquery.js"></s +cript> <script type="text/javascript">//<![CDATA[ $(function() { $("input[name='username']").bind("keyup", function(){ var myField = $(this); var myData = { username: myFiel +d.val() ,x: "ajax_lookup" }; $.ajax({ type: "GET" ,dataType: "json" ,url: "[% self_uri %]" ,data: myData ,success: function(json){ if ( json.username ) { $("input[name='fish'] +").val(json.fish); } else { $("input[name='fish'] +").val(""); } } }); }); }); //]]> </script> </head> <body> <form action="[% self_uri %]" method="post"> <fieldset> <legend>Simplistic Ajax Example</legend> <p> <label for="username">Username</label> <input id="username" type=" +text" name="username"/> <label for="fish">Fish</label> <input id="fish" type="text" name=" +fish"/> </p> <p style="clear:both; font-size:9px"> Usernames you can search for: <i>[% usernames.join(", ") %]</i> +. </p> </fieldset> </form> </body> </html>

    There are some Perl idioms in there to make the thing terse enough for a small sample. So, just to explain: the __DATA__ section is a Template Toolkit template. Check the docs if it doesn't make sense.

    The CGI proper does exactly three things; all different. If called without args, or with x=show_form, it prints the template which has a form in it. It sends two pieces of data to the template: self_uri (its own URI for the Ajax call to use) and usernames (an array ref from our "DB" of users to help prompt the person testing the form).

    Instead of names and addresses, I've given each username a fish. When the page loads, the jQuery installs a keyup listener on the username field: $("input[name='username']").bind("keyup", function()...). You can read up on how jQuery selectors work. It's easy and intuitive. It's just CSS for the most part.

    The next part is the Ajax. Ajax traditionally used XML, hence the "X." But I find JSON as a data layer superior in almost all cases. JSON::XS is perhaps the fastest data serializing available from Perl. So, read some of this page, jQuery/Ajax, to see what's going on. It should be pretty obvious.

    Our Ajax call will GET data of the type json from the same URL the CGI displays the form with the arguments x=ajax_lookup (which sub to run in the CGI) and username=[whatever the value of the username field is on the last keyup].

    The CGI then uses the username to look in the "DB" and returns a JSON data object like { username: "found user", fish: "Fish type" } if a username matches or {} if none does. The results get sent to the success: function(json){...} which is installed into our Ajax call. If the JSON data has a username, then its "fish" is put into the other form field. If not, the field is emptied. Ta!

    The third handler, no_such_action, is just there for code "completeness." Arbitrary user input should never be able to break an application.

    If this were a real application I'd install an "intent" layer in the keyup binding. You probably don't want to lookup the users on every keystroke. Just the one which is followed by a brief pause, probably, meaning the user is done typing (similar to hover intent stuff for mouseovers on menus so they don't flicker on and off but allow for friendlier UI). There are jQuery plugins for this -- I used one a year ago and can't remember its name.

    As you can see, it's quite easy, just confusing until you know how it all hangs together. Something like Catalyst underneath it makes it easy to extend it all over the place. Hope that helps get you (and anyone else) going. :)

Re: CGI how to lookup a value when a Field is filled in
by lostjimmy (Chaplain) on Dec 05, 2008 at 17:54 UTC

    I think what you are talking about is filling in the contents of the form without refreshing the entire page. To do this, you need to do some reading up on AJAX, specifically the XMLHttpRequest object. A good place to start is at the w3schools AJAX Intro.

    The oversimplified answer is that in the onChange event of the input field, you set up the request, which will call your Perl script on the server. When the results come back, you parse the results and fill out the rest of the form. It's not at all difficult to do something this simplified, but for anything more complicated you'd be better off using a javascript library like Prototype or dojo.

    Edit: Added AJAX tutorial link

Re: CGI how to lookup a value when a Field is filled in
by dsheroh (Monsignor) on Dec 05, 2008 at 20:37 UTC
    In the broader sense, lostjimmy's response is entirely correct. You need to use AJAX to do what you describe and he explained what goes on behind the scenes when you do so.

    To actually make all this happen, take a look at the CGI::Ajax module on CPAN. It provides a very easy way to set up an interface for Javascript events to trigger Perl functions and display the results.

      Hi, I am new to AJAX. I have tried the CGI AJAX Module but I am not getting successful in this and finding it very complex.

      I have tried the example given on link :
      http://www.perl.com/pub/a/2006/03/02/ajax_and_perl.html?page=2

      But it is not working in the desired way.

      It would be a great help if you could provide some working easy example to understand the basics of CGI::AJAX.
      It would help to build up the basics for AJAX.

      Many thanks!!
      -Vivek

        You are making it hard for us to help you better. "It is not working the desired way" does not tell us anything about how the code from the linked article fails for you. Please show the relevant code of your program, your input and the output you get.

        Ajax is still simple HTTP interaction, so writing an "Ajax script" is not different from writing any other CGI script, except that the output is JSON or some other format than HTML, and that the requesting client is likely driven by a JavaScript program.

Re: CGI how to lookup a value when a Field is filled in
by Your Mother (Archbishop) on Dec 05, 2008 at 21:00 UTC

    This is really quite easy once you know how to do it. The problem is, there are a lot of moving pieces so it can be very difficult the first time you try. You'll want to read up on HTTP and XMLHttpRequest as lostjimmy recommends and DHTML. Though do not try to do the requests yourself. They are painful, difficult, and verbose to do in a x-platform way. Use a JS lib. I personally recommend jQuery. I've worked with a few of them and it's the most facile and Perl-like to me.

    Before you proceed however, make sure that the feature isn't a security risk, which it sounds like to me. If, for example, I could put javahead into a field and automatically see personal information on PM, it would be a problem. :) If this is for an app like an HR frontend or something which only authorized individuals will be using, then it would be a nice feature. Otherwise, it's probably something to avoid.

    (update: I wrote a little sample script doing a simplistic version of this stuff. I'll post it later today when I have time to add comments and info.)