Beefy Boxes and Bandwidth Generously Provided by pair Networks
Pathologically Eclectic Rubbish Lister
 
PerlMonks  

Using CGI::Ajax for multiple form buttons/divs simultaneously

by Polyglot (Chaplain)
on Mar 18, 2018 at 16:54 UTC ( [id://1211196]=perlquestion: print w/replies, xml ) Need Help??

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

First, I am not using jQuery, Catalyst, or similar tools because of their bloated nature. I require a bandwidth-efficient solution due to poor internet access among the primary clients of the web tool. AJAX has shown itself to work acceptably in my case with a single <div>, but I need more than one and have found no examples of such anywhere. Experimentation has yielded no success either.

Here's what I have that works:

my $cgi = new CGI; my $pjx = new CGI::Ajax( 'getBibCompare' => \&comparisonBible_function, 'skip_header' => 1, ); $pjx->skip_header(1); $pjx->cgi( $cgi ); print $pjx->build_html( $cgi, \&main, {-charset=>'UTF-8', -expires +=>'-1d'} ); $pjx->JSDEBUG(1); sub main { #. . . $html = &editScreen; return $html; } # END SUB main

Accompanied by the HTML…

<div> <h3>SCRIPTURE COMPARISON</h3> <div id="dcomparison_text" name="dncomparison_text" style="border: 1 +px solid gray; width:300px; min-height:150px; background-color: #ffff +cc"> $compare_verse </div> <div id="Bible_Version_Menu" name="nBible_Version_Menu"> <select id="comparison_text" name="ncomparison_text" onchange="getB +ibCompare(['comparison_text'],['dcomparison_text'], 'POST'); "> . . .

I would like to add functionality to a separate button(s)/AJAX call that would come from a different form input, be associated to a separate Perl function, and direct its output to a different <div>, as shown below.

my $cgi = new CGI; my $pjx = new CGI::Ajax( 'getBibCompare' => \&comparisonBible_function, 'processFormInputs' => \&processFormInputs, 'editScreen' => \&editScreen, 'skip_header' => 1, ); $pjx->skip_header(1); $pjx->cgi( $cgi ); print $pjx->build_html( $cgi, \&main, {-charset=>'UTF-8', -expires +=>'-1d'} ); $pjx->JSDEBUG(1);
With the HTML . . .
<div id="top_menu" name="ntop_menu" class="flex-container topnav"> $TOP_MENU_OPTIONS </div> <select id="chapter" name="nchapter" onchange="processFormInputs(['boo +k'],['top_menu'], 'POST'); this.form.submit();"> <option value="">Chapter</option> $bookchaptercountoptions </select> <select id="verse" name="nverse" onchange="processFormInputs(['verse'] +,['top_menu'], 'POST'); this.form.submit();" /> <option value="">Verse</option> $chapterversecountoptions </select> <input type="submit" id="prev_verse" name="nprev_verse" value="<-- Pre +v Verse" $pv onclick="processFormInputs(['prev_verse'],['top_menu'], 'POST'); t +his.form.submit();" /> <input type="submit" id="next_verse" name="nnext_verse" value="Next Ve +rse -->" $nv onclick="processFormInputs(['next_verse'],['top_menu'], 'POST'); t +his.form.submit();" />

When I have managed to get any output from this at all to appear on the screen, it appears in the <div> for the comparison text, and not in the one for the menu as it should. (The menu is updating its own options based on queries to the database to populate the option lists.) Any ideas for how to proceed would be much appreciated.

Blessings,

~Polyglot~

Replies are listed 'Best First'.
Re: Using CGI::Ajax for multiple form buttons/divs simultaneously
by poj (Abbot) on Mar 18, 2018 at 18:51 UTC
    I would like to add functionality to a separate button(s)/AJAX call that would come from a different form input, be associated to a separate Perl function, and direct its output to a different div

    It's difficult to understand what you mean without a complete script so maybe this simple example will help.

    #!/usr/bin/perl use strict; use CGI::Ajax; use CGI; my $cgi = new CGI(); my $pjx = new CGI::Ajax( 'function1' => \&myfunction1, 'function2' => \&myfunction2, # 'skip_header' => 1 ); print $pjx->build_html($cgi,\&main, {-charset=>'UTF-8', -expires=>'-1d'}); sub main { my $html = <<EOT; <HTML> <HEAD><title>CGI::Ajax Example</title></HEAD> <BODY> <div id="div1_text">Number = </div> <div id="div2_text">Letter = </div> <select id="select1" onchange="function1(['select1'],['div1_text'], +'POST'); "> <option value="">Select Number</option> <option value="1">1</option> <option value="2">2</option> <option value="3">3</option> </select> <select id="select2" onchange="function2(['select2'],['div2_text'], +'POST'); "> <option value="">Select Letter</option> <option value="A">A</option> <option value="B">B</option> <option value="C">C</option> </select> </BODY> </HTML> EOT return $html; } sub myfunction1 { return "Number = ".shift }; sub myfunction2 { return "Letter = ".shift };
    poj

      I appreciate the response, but I'm still struggling with the concepts. What you have outlined above is what I've been trying to do all along. But it just doesn't seem to work with more than one div at a time.

      Here's a more descriptive (I hope) explanation of what I am trying to accomplish:

      • Create a page with seven controls
        1. Select menu with 66 books
        2. Select menu with number of chapters for book currently selected
        3. Select menu with number of verses for chapter currently selected
        4. "Previous" button to navigate back one verse
        5. "Next" button to navigate forward one verse
        6. Select menu for version comparison
        7. "Update" button to submit edited text from two textarea fields, following which all options progress to the next verse
      • After any one of the seven controls is clicked/changed, update the respective fields accordingly.

      The first five controls can all form part of the "top_menu" div, and any update affecting one of the first three will affect any others in its downstream, as well as affecting the textarea fields and comparison field. Any change to the comparison field should affect it only, leaving everything else unchanged. Coding this without AJAX seems doable, but the AJAX complicates things tremendously for me. I just cannot seem to wrap my mind around how to organize the functions to accomplish it in a way that the AJAX would accept.

      Blessings,

      ~Polyglot~

        Ok, taking just the first 2 points try this to see if I understand you correctly

        #!/usr/bin/perl use strict; use CGI::Ajax; use CGI; my $cgi = new CGI(); my $pjx = new CGI::Ajax( 'function1' => \&myfunction1, 'function2' => \&myfunction2, # 'skip_header' => 1 ); print $pjx->build_html($cgi,\&main, {-charset=>'UTF-8', -expires=>'-1d'}); sub main { my $html = <<EOT; <HTML> <HEAD><title>CGI::Ajax Example</title></HEAD> <BODY> <div id="div2_text">Chapter = </div> BOOK <select id="select1" onchange="function1(['select1'],['div1_text'], +'POST'); "> <option value="">Select Book</option> <option value="1">1</option> <option value="2">2</option> <option value="3">3</option> </select></br> CHAPTER <div id="div1_text"></div> </BODY> </HTML> EOT return $html; } sub myfunction2 { return "Chapter = ".shift }; sub myfunction1 { my $book = shift; my @chap = (undef,['1A','1B','1C'], ['2A','2B','2C','2D'],['3A','3B']); my $option; for (@{$chap[$book]}){ $option .= qq!<option value="$_">$_</option>!; } return qq! <select id="select2" onchange="function2(['select2'],['div2_text'], +'POST'); "> $option </select>!; };
        poj
Re: Using CGI::Ajax for multiple form buttons/divs simultaneously
by Your Mother (Archbishop) on Mar 19, 2018 at 13:27 UTC
    I am not using jQuery, Catalyst…bloated nature. I require a bandwidth-efficient solution due to poor internet access…

    Catalyst is only "bloated" in server-side RAM usage. Bandwidth does not come into it. jQuery is larger than several pure Ajax libs but smaller than plenty of others—minified and gzipped it's going to be a one time cost of around 30K—and if you use a version from a CDN like Google's, is guaranteed to be fast and depending on the clients, possibly already cached in the browser. Putting the most information in a single exchange, as opposed to direct triggering of snippets, is the most bandwidth friendly approach; unless you move to websockets or something similar (Corion had something related and neat here that I never followed-up to learn more about and can't quite remember. <- Update: I was remembering something that wouldn't be of help here).

    Not saying your approach is inappropriate at all. Sometimes closer to the metal is necessary. Just wanted to balance the cat skinning considerations. :P

      I appreciate the contribution of your opinion. A nice website I found addressing the impact of jQuery's size was this one:

      https://modernweb.com/is-jquery-too-big-for-mobile/.

      That website backs up my assertions regarding the benefit of avoiding jQuery. I know my target audience and situation. My clients will be mostly on older Android phones in an otherwise 1 Mbps-dominated third-world country, and they will be accessing this application through a server on the other side of the world, almost maximizing the latency of the connection. Given the particular circumstances I'm dealing with, it's worth the effort for me to make AJAX work. Plus, I get to learn how to implement AJAX.

      Blessings,

      ~Polyglot~

        That website backs up my assertions

        It also says know/test your audience, and that amazon and other big money sites should be super fast

        Every other day I visit the big money sites amazon/ebay/walmart.. and they're all much much much slower than the worst case scenario presented in that website

        So avoiding jquery cause some random website argues its costing you sales based on ancient nonsense , is not a good way to back up your assertions , it simple confirmation bias

      As I am now getting things nearly functional (the AJAX part is working except for the UTF8 encoding issues that still remain), I have been able to extract the JavaScript created by CGI::Ajax and save it to a separate file, then gzip it for proper size comparison. It amounts to 2.1 KB. That is at least 14 times smaller than the jQuery option you mentioned. Given the thousands of connections which this project will require, that works out to more than a negligible bandwidth savings.

      Blessings,

      ~Polyglot~

Re: Using CGI::Ajax for multiple form buttons/divs simultaneously
by dsheroh (Monsignor) on Mar 19, 2018 at 12:03 UTC
    It's not entirely clear what you're asking, but I think you want to make a single AJAX call to the Perl back end, then use the (single) response to populate multiple divs on the displayed page. Is that correct?

    If so, this is covered by the second usage example in the CGI::Ajax documentation. Just return a list of multiple values from the Perl handler sub (return 'first', 'second';), specify a list of divs on the HTML side (onclick="processFormInputs(['next_verse'],['first_div', 'second_div'], 'POST');), and you should be set.

    If you need something more specialized than that, the third usage example shows the syntax to tell CGI::Ajax to pass the returned value(s) off to an arbitrary javascript function, where you can fold, spindle, and mutilate the data to your heart's content, then assign the pieces to whatever divs you like, including a basic example of a javascript function which might be used to do this.

    (Note that it's been several years since I last used CGI::Ajax, so I could be misremembering some of the details, but the documentation matches up with what I think I remember, so it's probably at least close to correct.)

      I've made significant progress in the last hour, and I just wanted to thank you for giving me one of the most important clues so far--that of returning multiple values from the subroutine. I am still struggling, however, with the AJAX apparently not reading all of the form variables upon callback to the Perl script, which means I am not able to access the correct data to put into the div. The div is being updated with the correct translation/version, but it is doing so with the default record number (1) instead of the currently selected record from the menu (a separate div, one which is not invoked with the comparison div).

      I have been attempting to pass this information via a hidden form field, but it seems to be lost in transit using AJAX on an unrelated div. Am I expecting the wrong thing?

      Blessings,

      ~Polyglot~

      I'm discovering that it's more complicated for me than perhaps for some others on account of UTF8 encoding issues. I need to decode text coming in from the form, but with the AJAX apparently connecting to just the specified function/subroutine from the perl script, even the initial "my/our" variable declarations seem to be bypassed on the callback, and the code ends up trying to decode the UTF8 characters twice--which either results in an error message, or (without decoding), text that is incorrectly displayed. I wish I understood exactly what the AJAX is doing and/or needing to do in terms of following the code.

      I still seem to be no further along than I was at the start. However, I may have to run some experiments on text using pure English….so much for being a polyglot!

      Blessings,

      ~Polyglot~

        Yeah, that is the one major annoyance with CGI::Ajax, it kind of takes over completely. When you call build_html, it actually spits out a big mess of javascript into your page to make its magic work, and then, when you call one of its javascript helper functions, it intercepts that and takes over the back-end request handling, bypassing whatever your normal dispatch method might be. Towards the end of my time using CGI::Ajax, I delved into that and managed to at least partially unravel it and figure out how to bypass some of the magic, but I don't recall any of the details, only that I did it.

        These days, I'm using Dancer and Dancer::Plugin::Ajax, which requires me to manually write a little bit of javascript to invoke the ajax functionality, but it's much more transparent and fits into the rest of the application's framework, so I feel it ends up being a lot easier to work with overall. But that might not be an option for you if you're already dealing with legacy CGI scripts and aren't in a position to migrate to Dancer.

Re: Using CGI::Ajax for multiple form buttons/divs simultaneously
by bliako (Monsignor) on Mar 20, 2018 at 12:21 UTC

    I am not sure what other fellow monks' experience is with the AJAX perl module but myself so far, have managed without it, successfully producing beautiful and succinct code.

    The first thing I needed were server-side perl CGI scripts responsible for querying DBs and sending data back to client.

    Secondly, in all my HTML files I have a small javascript function for POSTing or GETing form data to my CGI scripts using AJAX. Such functions (using jQuery OR NOT) can be found on the net, for example here is one random script using pure javascript (I have neither used nor tested it): https://wp-time.com/ajax-form-pure-javascript-without-jquery/ . Let's call this function doAjax(params, url)

    Thirdly, I create higher-level javascript functions responsible for making a POST/GET request (via the doAjax()) and if/when data is received, filling the appropriate DIV or appending to the appropriate UL list, etc.

    The procedure is the following, in a single HTML page you have one or more buttons which onClick() will initiate a POST/GET to one of your CGI scripts. When pressing the button in your HTML for, say, doing X, you supply the POST/GET params and also a destination DIV. Then you call the higher-level javascript function I mentioned, let's call it doX(params, destination_div). The doX() function will first call the doAjax() which will have a callback which will be called when data from your server-side CGI script has been received by the client's browser. This is where "filling the content" part of your doX() function starts. You already gave it a destination DIV or UL, it has got the data from the server and so it goes and adds the content in your client's current HTML page (without reloads etc.) by the magical DIV.innerHTML = "<b>voice from the bush</b>"

    Because of its asynchronous nature, it is possible for one single HTML page, after it has been loaded in client, to contain buttons for user to request data from server CGI scripts (when I say buttons I mean POST/GET forms or just buttons with an onclick() method calling the doX() functions), data arriving and HTML page's content changing without page reload. In effect you can have a complete surfing session without ever leaving or reloading your current HTML page!

    In my experience, it is possible and easy to do AJAX without jQuery and CGI::AJAX. I would first go down this path before using other modules. I have gone down that path in the past and it was success.

    A word of warning: Ajax's eventual fate was a tragic one. Here is how Exekias saw it 2500 years ago : https://en.wikipedia.org/wiki/Suicide_of_Ajax_vase#/media/File:Exekias_Suicide_d_Ajax_01.jpg

Re: Using CGI::Ajax for multiple form buttons/divs simultaneously
by Anonymous Monk on Mar 19, 2018 at 18:17 UTC
    AJAX is initiated on the client side and it has nothing directly to do with <DIVs nor with any other HTML or DOM structures. It consists of making an asynchronous HTTP request ("XHR"), which sends a payload via either GET or POST to a specified host-side URL. When the host sends its reply, a callback subroutine (in JavaScript), specified when the XHR was issued, will be invoked. This callback will be given both the HTTP error-code and the entire content of the host's response, which serves as its input data. This response will not appear anywhere on the screen unless the callback routine, in response to whatever it has received, chooses to manipulate the DOM in some visible way. You can manage XHR-requests on the client side using a piggish library like JQuery, or with a roughly 20-line JavaScript function or object which is available anywhere on the Internet.

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: perlquestion [id://1211196]
Approved by Corion
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others lurking in the Monastery: (2)
As of 2024-04-19 18:44 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found