http://qs1969.pair.com?node_id=1098628

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

I'm just starting out learning how to use the Dancer2 framework and I'm having problems with figuring out how to configure the route to display the returned data with or without the use of the Dancer2::Plugin::Ajax module.

For my initial test the route simply returns "ajax test" but the goal is to return the results of a DB query.

package Templar; use DBI; use Dancer2; #use Dancer2::Plugin::Ajax; use Dancer2::Plugin::Database; use Template; use HTML::Escape qw/escape_html/; use Crypt::SaltedHash; use Data::Dumper; our $VERSION = '0.1'; hook before => sub { if (!session('logged_in') && request->dispatch_path !~ m{^/login}) + { return forward '/login', { requested_path => request->dispatch +_path }; } }; #ajax '/p_details' => sub { "ajax test" }; get '/p_details' => sub { "ajax test" };

If I use the ajax route, I get a "404 Not Found" error for the p_details page/route.

The p_details.tt template is:

<script> $(document).ready(function() { $("#folder").find("[id^='tab']").hide(); // Hide all content $("#tabs li:first").attr("id","current"); // Activate the first ta +b $("#folder #tab1").fadeIn(); // Show first tab's content $('#tabs a').click(function(e) { e.preventDefault(); if ($(this).closest("li").attr("id") == "current"){ //detectio +n for current tab return; } else{ $("#folder").find("[id^='tab']").hide(); // Hide all content $("#tabs li").attr("id",""); //Reset id's $("#tabs li").removeAttr("id"); $(this).parent().attr("id","current"); // Activate this $('#' + $(this).attr('name')).fadeIn(); // Show content for +the current tab } }); partner_details('general'); }); </script> <span id="top"> <h2>Partner Details</h2> <h3>The partner details function is still in development and is not ye +t ready to be used.</h3> </span> <ul id="tabs"> <li><a href="#" onclick="partner_details('general')">General</a></li +> <li><a href="#" onclick="partner_details('qualifier')">EDI Qualifier + / ID</a></li> <li><a href="#" onclick="partner_details('certificates')">Certificat +es</a></li> <li><a href="#" onclick="partner_details('contact')">Contact Informa +tion</a></li> <li><a href="#" onclick="partner_details('notes')">Notes</a></li> </ul> <div id="folder"> </div>

The javascript is:

function partner_details(catagory) { if (! xmlHttpObj) { init_ajax(); } if (xmlHttpObj) { var url = '/p_details'; xmlHttpObj.open('GET', url, true); xmlHttpObj.onreadystatechange = partner_tab(catagory); xmlHttpObj.send(null); } } function partner_tab(tab) { var details = { general: "tab1", qualifier: "tab2", certificates: "tab3", contact: "tab4", notes: "tab5" }; //alert(details[tab]); // status is always 0 //alert('response status:' + xmlHttpObj.status); // xmlHttpObj.responseText is always empty //document.getElementById('folder').innerHTML = xmlHttpObj.respons +eText; // this updates the div as expected //document.getElementById('folder').innerHTML = "<table><tr><td>" ++ details[tab] + "</td></tr></table>"; if (xmlHttpObj.readyState == 4 && xmlHttpObj.status == 200) { // we never reach this point alert(details[tab]); document.getElementById('folder').innerHTML = xmlHttpObj.respo +nseText; //document.getElementById('folder').innerHTML="<table><tr><td> +" + details[tab] + "</td></tr></table>"; } }

Firebug shows that the status is "200 ok", Content-Type is "text/html", Content-Length is 9, and both Response and HTML show the expected "ajax test", but javascript readystate and status never change so the updating of the div or the alert window never take place.

My first thought was that I had an error in the javascript, but it works correctly in a php app. So, that means that I'm doing something wrong in the route but the Dancer2 documentation in the ajax/jquery area is almost non existent so I haven't been able to figure out the proper syntax.

Replies are listed 'Best First'.
Re: How to make ajax requests in Dancer2
by Corion (Patriarch) on Aug 26, 2014 at 16:13 UTC

    Have you compared the output of your PHP app with the output of your Perl application?

      Are you referring to comparing what firebug shows or the rendered pages?

      I probably should have explained that the php app is not a duplication of this perl app. The javascript is a direct copy of what I'm using in the php app with the only difference being the url assignment. On the php side, firebug is showing the same type of successful results i.e., status 200, content-type and length are what's expected and the Response and HTML sections are showing the correct/expected results.

      The only difference I see is in the final rendered page where the document.getElementById('folder').innerHTML = xmlHttpObj.responseText; is being executed in the php app, but not in the perl app. I'm not seeing any errors or anything else in firebug that would account for this, but I have to admit that I don't full understand some of the info it's providing.

      My knowledge of javascript and ajax/jquery is very limited which makes troubleshooting that portion of the app frustrating.

        If the error only occurs with the Perl side, then there must be a difference in what Perl returns when compared with what PHP returns. Inspect both parts until you find the difference.

        Consider maybe returning a static JSON file instead of using Perl to find out what the Javascript breaks on.

Re: How to make ajax requests in Dancer2
by fishmonger (Chaplain) on Aug 26, 2014 at 20:45 UTC

    Ok, I found the solution which utilizes the Dancer2::Plugin::Ajax module and is much cleaner.

    The route is now

    ajax '/p_details/:tab'        => sub { return params->{tab}; };

    I dropped the 2 javascript functions (partner_details and partner_tab) previously posted and am now using this function.

    function partner_details(tab) { $.ajax({ url: "/p_details/" + tab, success: function( data ) { $( "#folder" ).html( data ); } }); };

    That other js function was moved to the external js file to remove the clutter in the template file, which is now:

    <script> init_folder_tabs(); </script> <span id="top"> <h2>Partner Details</h2> <h3>The partner details function is still in development and is not ye +t ready to be used.</h3> </span> <ul id="tabs"> <li><a href="" onclick="partner_details('general')">General</a></li> <li><a href="" onclick="partner_details('qualifier')">EDI Qualifier +/ ID</a></li> <li><a href="" onclick="partner_details('certificates')">Certificate +s</a></li> <li><a href="" onclick="partner_details('contact')">Contact Informat +ion</a></li> <li><a href="" onclick="partner_details('notes')">Notes</a></li> </ul> <div id="folder"></div>

    So, now when I click on the "folder tab" link, it displays the tab name as the main content of the "folder". Now I just need to write the sub that queries the db and builds the table to be displayed.