Jeffrey Tackett Jeffrey Tackett - 3 months ago 29
Perl Question

jquery.js not referencing properly within Perl Template Toolkit-generated files

I have an extremely simple little JavaScript/Perl CGI example that I've used to get started with a larger project. When I run it as

client.html
and
server.pl
, it works flawlessly. However, when I change the
client.html
to
client.tmpl
, and call it from the same
server.pl
script using Template Toolkit, it can't seem to find jQuery functions.

I have even created a
master.tmpl
file, and used
[% INCLUDE client.html %]
inside it, and it fails. The browser console verifies that the path to
jquery.js
is correct, but it's like it fails to load it when it's inside a template.

The following is the HTML file that I'm essentially trying to turn into a
.tmpl
file (formatting messed up, first time here, sorry):

client.html



<!DOCTYPE html>
<html>
<head>
<title>AJAX Example</title>
<meta charset="UTF-8" />
<script src="http://domainname/ajax_example/jquery.js"></script>

<script type="text/javascript">
function myTimer() {
var typingTimer;
var doneTypingInterval = 2000;

$("#user_text").keyup( function() {

clearTimeout(typingTimer);

if ( $('#user_text' ).val()) {
typingTimer = setTimeout(updateText, doneTypingInterval);
}
});

function updateText() {

var current_text = document.getElementById('user_text').value;
var http = new XMLHttpRequest();

http.onreadystatechange = function() {

if ( http.readyState == 4 && http.status == 200 ) {
var response = http.responseText;
document.getElementById('server_response').value = response;
}
};

http.open("GET", "http://domainname/ajax_example/cgi-bin/server.pl?user_text=" + current_text , true );

http.send();
}
}

</script>
</head>

<body>
<div>Input Text: <input type="text" id="user_text" name="user_text" onkeyup="myTimer()"/></div><br/>
<div>Server Resp.: <textarea id="server_response" name="server_response"> </textarea></div>
<br/>
</body>
</html>


The
server.pl
that works:

server.pl



$cgi = CGI->new;
$id = $cgi->param('user_text');
$result = uc($id);
print $cgi->header();
print $result;


The
server.pl
that doesn't work:

server.pl



$cgi = CGI->new;
$id = $cgi->param('user_text');
**returned from result calculation sub** $result = uc($id);

my $config = {
EVAL_PERL => 1,
POST_CHOMP => 1,
INTERPOLATE => 1,
INCLUDE_PATH => '/usr/lib/cgi-bin/ajax_example/:/var/www/html/ajax_example/',
};

print $cgi->header( -charset=>'utf-8' );
my $tt = Template->new($config);
$tt->process('client.tmpl', \$result);
}


Keep in mind, I am trying my best to summarize the code, but the Perl and JavaScript work just fine, unless it's being used through TT. The error is:

#user_text.keyup is not a function:
("#user_text").keyup(function(){


Same error I would get if I put in a bad path to
jquery.js
. The path is good though, without a doubt.

Thank you for any recommendations anyone can provide.

Answer

The immediate problem is that you have enabled the INTERPOLATE option, which interpolates Perl variables anywhere in the template. That makes the module attempt to replace $( by its value, and destroys the JavaScript syntax

It's a sloppy way of using templates anyway: you should pass all the values you need in the $vars hash, and extract them from there using [% variable %] template directives. The same applies to the EVAL_PERL option, as any complex data manipulation should ordinarily be in the code that calls process. Everything you need to do inside the template is available as a Template directive

Talking of the $vars hash, you should be getting Not a HASH reference errors, because you are passing to process a reference to the string variable $result instead of a hash containing that value. It's unclear how you want that value to be handled, but the only mention of id in your HTML is the id attribute of the <input> element at the bottom of the HTML, so I've put a directive in their to show you how it all works

Take a look at this code

CGI program

use strict;
use warnings 'all';

use CGI;
use Template;

my $cgi    = CGI->new;
my $id     = $cgi->param('user_text') // 'abc123';
my $result = uc $id;

print $cgi->header( -charset => 'utf-8' );

my $tt = Template->new( {
    # INCLUDE_PATH => '/usr/lib/cgi-bin/ajax_example/:/var/www/html/ajax_example/', 
    POST_CHOMP   => 1,
} );

$tt->process('client.html', { result => $result } );

I have modified your HTML file like this. I couldn't tell what you wanted to do with the value that the CGI code pulls from the user_text parameter, so I put it into a value attribute for the first input field

Template file

<!DOCTYPE html>
<html>

    <head>

        <title>AJAX Example</title>
        <meta charset="UTF-8" />

        <script src="http://domainname/ajax_example/jquery.js" />

        <script type="text/javascript">

            function myTimer() {

                var typingTimer;
                var doneTypingInterval = 2000;

                $("#user_text").keyup( function() {

                   clearTimeout(typingTimer);

                   if ( $('#user_text' ).val() ) {
                       typingTimer = setTimeout(updateText, doneTypingInterval);
                   }
                } );

                function updateText() {

                    var current_text = document.getElementById('user_text').value;
                    var http = new XMLHttpRequest();

                    http.onreadystatechange = function() {

                        if ( http.readyState == 4 && http.status == 200 ) {
                            var response = http.responseText;
                            document.getElementById('server_response').value = response;
                        }
                    };

                    http.open("GET",
                        "http://domainname/ajax_example/cgi-bin/server.pl?user_text=" + current_text,
                        true );

                    http.send();
                }
            }

        </script>

    </head>

    <body>

        <div>Input Text:
            <input type="text" id="user_text" name="user_text" value="[% result %]" onkeyup="myTimer()"/>
        </div>
        <br/>

        <div>Server Resp.:
            <textarea id="server_response"  name="server_response"/>
        </div>
        <br/>

    </body>

</html>

And here's the resulting output from the CGI code. As you can see, the $("#user_text").keyup call remains intact, and the value from the CGI code—the result element passed in the $vars hash—has been substituted into the value attribute of the text input element

I hope this helps you to progress and get your application working

output

Content-Type: text/html; charset=utf-8

<!DOCTYPE html>
<html>

    <head>

        <title>AJAX Example</title>
        <meta charset="UTF-8" />

        <script src="http://domainname/ajax_example/jquery.js" />

        <script type="text/javascript">

            function myTimer() {

                var typingTimer;
                var doneTypingInterval = 2000;

                $("#user_text").keyup( function() {

                   clearTimeout(typingTimer);

                   if ( $('#user_text' ).val() ) {
                       typingTimer = setTimeout(updateText, doneTypingInterval);
                   }
                } );

                function updateText() {

                    var current_text = document.getElementById('user_text').value;
                    var http = new XMLHttpRequest();

                    http.onreadystatechange = function() {

                        if ( http.readyState == 4 && http.status == 200 ) {
                            var response = http.responseText;
                            document.getElementById('server_response').value = response;
                        }
                    };

                    http.open("GET",
                        "http://domainname/ajax_example/cgi-bin/server.pl?user_text=" + current_text,
                        true );

                    http.send();
                }
            }

        </script>

    </head>

    <body>

        <div>Input Text:
            <input type="text" id="user_text" name="user_text" value="ABC123" onkeyup="myTimer()"/>
        </div>
        <br/>

        <div>Server Resp.:
            <textarea id="server_response"  name="server_response"/>
        </div>
        <br/>

    </body>

</html>