jQuery and Catalyst: Easier than you thought

user-pic

Wiki Extras for this post

So recently, I've been hacking on some jquery for some UI stuff for a small site I've been working on.  The site is backed by Catalyst, of course, and given I'm not big on design stuff, I figured I'd give jQuery UI a go.  It turned out great, however, my findings were that when using the jQuery AJAX (or for the pedantic, AHAH) method for generating tabs, one has to do some tinkering from the standard Template::Toolkit WRAPPER practices. 

Typically, one writes a wrapper as such:

[% IF template.name.match('\.(css|js|txt)') || no_wrapper;
   debug("Passing page through as text: $template.name");
     content;
   ELSE %]
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<title>[% template.title or "myapp.com" %]</title>
<link rel="stylesheet" href="[% c.uri_for('/static/styles/main.css') %]" />
<link rel="shortcut icon"  href="[% c.uri_for('/static/images/favicon.ico') %]" />
<style>
.titlec {
  font-size: small;
}
ul.videos li {
  float: left;
  width: 10em;
  margin-bottom: 1em;
}
ul.videos
{
  margin-bottom: 1em;
  padding-left : 0em;
  margin-left: 0em;
  list-style: none;
}
</style>

</head>
<body>
<div id="header" align="center">
<img src="[% c.uri_for('/static/images/logo.png') %]" />
</div>

<div id="topnav">

<div id="dividermenu">
<ul>
<li><a href="[% c.uri_for('/') %]">home</a></li>
[%- UNLESS c.user_exists %]
<li><a href="[% c.uri_for('/login') %]">login</a></li>
<li><a href="[% c.uri_for('/signup') %]">sign up</a></li>
[%- ELSE -%]
<li><a href="[% c.uri_for('/logout') %]">logout</a></li>
<li><a href="[% c.uri_for('/photo/add') %]">upload photos</a></li>
[%- END %]
<li><a href="[% c.uri_for('/photos') %]">view pictures</a></li>
<li><a href="[% c.uri_for('/search') %]">search pictures</a></li>
</ul>
</div>
</div>

<div id="bodyblock" align="right">
<div id="left-col">
<div id="menu">
<ul><h2>Products</h2>
<ul>
<li><a href="[% c.uri_for('/') %]">Home</a></li>
<li><a href="[% c.uri_for('/contact') %]" title="Contact Us">Contact Us</a></li>
<li><a href="[% c.uri_for('/about') %]" title="About Us">About</a></li>
[% UNLESS c.user_exists %]
<li><a href="[% c.uri_for('/signup') %]">sign up and upload your pictures</a></li>
[% END %]

</ul>

</div>
</div>
<div class="error">[% error_msg %]</div>
<div class="status">[% status_msg %]</div>
<div id="content">
<p>
[% content %]
</p>

</div>
</div>
<div id="footer" align="center">Copyright (c) myapp.com</div>

</div>

</body>
</html>
[% END %]


However, jQuery sets things up so that your main page acts as your wrapper.  Essentially, you have to include the tab set up in your index/main page template as opposed to your wrapper.  Otherwise, you end up with nested content that looks horrid. 

Long story short:  It's easiest to lose the wrapper in this case.  Don't include one for your Template::Toolkit view, and just do your page layout in your main template.  Here's what I came up with: 

"index.tt"

[% IF template.name.match('\.(css|js|txt)') || no_wrapper;
   debug("Passing page through as text: $template.name");
     content;
   ELSE %]
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<title>[% template.title or "My App's Website - Now With More jQuery!" %]</title>
<link type="text/css" href="[% c.uri_for('/', 'static', 'css', 'custom-theme', 'jquery-ui-1.7.2.custom.css') %]" rel="stylesheet" />
<script type="text/javascript" src="[% c.uri_for('/', 'static', 'js', 'jquery-1.3.2.min.js' ) %]"></script>
<script type="text/javascript" src="[% c.uri_for('/', 'static', 'js', 'jquery-ui-1.7.2.custom.min.js' ) %]"></script>
<link rel="shortcut icon"  href="[% c.uri_for('/', 'static','images','favicon.ico') %]" />
<script type="text/javascript">
$(function(){

    // Accordion
    $("#accordion").accordion({ header: "h3" });

    // Tabs
    $('#tabs').tabs({
        ajaxOptions: { async: false },
        spinner: 'Retrieving data...', 
        collapsible: true
    });


    // Dialog           
    $('#dialog').dialog({
        autoOpen: false,
        width: 600,
        buttons: {
            "Ok": function() {
                $(this).dialog("close");
            },
            "Cancel": function() {
                $(this).dialog("close");
            }
        }
    });
   
    // Dialog Link
    $('#dialog_link').click(function(){
        $('#dialog').dialog('open');
        return false;
    });

    // Datepicker
    $('#datepicker').datepicker({
        inline: true
    });
   
    // Progressbar
    $("#progressbar").progressbar({
        value: 20
    });
   
    //hover states on the static widgets
    $('#dialog_link, ul#icons li').hover(
        function() { $(this).addClass('ui-state-hover'); },
        function() { $(this).removeClass('ui-state-hover'); }
    );
   
});
</script>

</head>
<body>

<div id="header" align="left">
<img src="[% c.uri_for('/', 'static','images','logo.gif') %]" />
</div>

[% IF status_msg %]
<div class="ui-widget">
    <div class="ui-state-highlight ui-corner-all" style="padding: 0pt 0.7em; margin-top: 20px;">
        <p><span class="ui-icon ui-icon-info" style="float: left; margin-right: 0.3em;"></span>

        <strong>Success</strong> [% status_msg %].</p>
    </div>
</div>
[% END %]

[% IF error_msg %]
<div class="ui-widget">
<div class="ui-state-error ui-corner-all" style="padding: 0pt 0.7em;">
    <p><span class="ui-icon ui-icon-alert" style="float: left; margin-right: 0.3em;"></span>
    <strong>Alert:</strong> [% error_msg %].</p>
</div>
[% END %]
[% content %]
</body>
</html>
[% END %]


"contact.tt"
<pre><h2>Contacting Us</h2>

<div>[% form.render %]</div>


I hope this helps future jQuery-ui users to lessen their confusion if they happen to run into this.  jQuery is a *great* thing and I have really enjoyed its ease of use on top of Catalyst.

No TrackBacks

TrackBack URL: http://www.catalyzed.org/mt/mt-tb.fcgi/60

3 Comments

| Leave a comment

I find it easier for situations like these to just set the body manually. Then TT never even processes the template. You know the spell:

$c->response->body("yadda yadda yadda");

Well, you know I've been asking my self for the right method doing ajax requests with TT. The solution which removes your

[% IF ajax_request %]...[% ELSE %] uglyness from your .tt is to create another TT View, for example AjaxTT

and

$c->forward('View::AjaxTT'); in the end of your controller action...

well you must set different extension for those templates for example .att :)

That's g00d for 2 more things .. you might don't want to process all your page action when using only aTT .. and also with jquery you might want to make your index page (tab) load via ajax, so your index.tt will be only a wrapper :)..

CHEERS :D

fREW: That's good thinking. However, there's still probably going to be a need for me to have TT in my page at some point, so perhaps drinchev's idea would be most optimal.

I'm by no means a jQuery expert, so this could certainly be up for discussion.

Leave a comment

All comments are moderated. Spammers don't waste your time

Sponsored By


Ionzero: Rescue your dev project.
OpenID accepted here Learn more about OpenID

Following

Not following anyone

Note to spammers: all comments are moderated. Don't waste your time