Getting Started with Catalyst and jQuery (and jQuery UI)

A few initial comments

For the duration of this article we'll be assuming that you want to use jQuery
and jQuery-UI with Catalyst, and that you'll be using the Template Toolkit for
your templating engine. Of course, this is perl and There's More Than One Way
To Do It -- so you can use something else instead of TT, or tweak our
suggestions a bit. If you do, things will probably Just Work. Have fun, and
stay DRY.

This article is aimed at developers new to Catalyst and especially Catalyst
with jQuery. It is designed to help someone go from 0 to App as quickly as
possible.

The commands in the article assume you're using Linux. If you aren't, adjust
the commands accordingly (or switch to Linux ;).

Background knowledge

This article assumes you already know some things. If you don't,
please follow the provided links to read about a few key topics before continuing
with this article. You can probably make it through without knowing some
of them, but if you're reading this, all of them will make your life easier.

That's fine, but it has one limitation that's sort of needless: it assumes
that the web interface is the only one we'll be making. As a matter of good
practice, let's just start off right and name it according to what we're
actually making: A web interface to our Pizzaria's software.

$ catalyst.pl Pizzaria::Web
created "Pizzaria-Web"
created "Pizzaria-Web/script"
created "Pizzaria-Web/lib"
created "Pizzaria-Web/root"
created "Pizzaria-Web/root/static"
created "Pizzaria-Web/root/static/images"
created "Pizzaria-Web/t"
created "Pizzaria-Web/lib/Pizzaria/Web"
created "Pizzaria-Web/lib/Pizzaria/Web/Model"
created "Pizzaria-Web/lib/Pizzaria/Web/View"
created "Pizzaria-Web/lib/Pizzaria/Web/Controller"
created "Pizzaria-Web/pizzaria_web.conf"
created "Pizzaria-Web/lib/Pizzaria/Web.pm"
created "Pizzaria-Web/lib/Pizzaria/Web/Controller/Root.pm"
created "Pizzaria-Web/README"
...

That's better. Now when we are ready to create other interfaces to our
application, it won't require any refactoring; we'll simply be able to add
Pizzaria::CLI, etc.

Because I like to, I always rename the app's directory to omit the "-Web":

$ mv Pizzaria-Web Pizzaria
$ cd Pizzaria

Setting up jQuery and jQuery UI

For ease, throughout the remainder of this article, I'll refer to jQuery and
jQuery UI as "jQuery". You may, of course, use jQuery without the UI libs,
and you may use the UI without ever manually utilizing jQuery (though it will
still be loaded as a dependency, of course), but for our purposes, I'll assume
that you're going to be using both.

Because jQuery is a client-side JavaScript library, most of the actual use of
it will be done through the templates. There's a lot we can do in Catalyst
to make things easier in jQuery, but we'll have to get to those later. Let's
get started.

The jQuery Frameworks

First, in setting up our repo, we'll create a place for jQuery to live.
Remember that whatever we put under the ./root/static directory will be
files that we're expecting to access directly with hyperlinks; that means
we can effectively consider this something like the "public API" of our
application resources. With that in mind, you want the names to be
semantically meaningful. And, as always, Do The Right Thing -- don't
take shortcuts =)

We'll have all our javascript live in ./root/static/javascript/.
Similarly, css will live in ./root/static/css/, images in
./root/static/images/, and so forth. Our templates will live in
./root/src/.

$ mkdir -p ./root/static/javascript/ext

We'll put all third-party javascript libs in ./root/static/javascript/ext/
and know never to edit anything in there (right? =) Separating external libs
that way will also help future maintainers and developers to understand how
the application is structured.

Next, we'll download the latest, greatest versions of jQuery and jQuery UI
from their respective web pages (linked above) and unzip them into those
directories. When you're done, your directory structure should look like
this:

Plugins (third-party and in-house)

One of the great things about jQuery is the very active user community. You
should get into the habit of using jQuery plugins liberally; if you choose
them well, they're light-weight, efficient, and very effective. Let's create
a place for our jQuery plugins to live.

Of course, if we start using other third-party JavaSCript plugins we can
just add them. For example, if we wanted to use CKEditor, as I did for a
project at work recently, or Flot, or any other third-party JS lib,
it would be very easy simply to add the spaces for it.

and we can just add files into those spaces as we need to. (I commented out
the commands below in case someone isn't reading carefully
and blindly copies/pastes all the commands in this article =)

We're also going to want to be developing internal plugins from time to
time, probably. Let's create a place for those to live as well, for the sake
of completeness.

$ mkdir -p ./root/static/javascript/plugins/jquery

Now when you want to create a generic jQuery plugin (that's not specific to
a particular part of your application's view domain) just drop it into that
directory.

Great -- now we're ready to get started with our app development.

Data is everything

Your data is everything. You can always fix a bad flow of logic in your
controller, or even fix a bunk interface to your models... but if you have
bad data, you're stuck. For that reason, you should be thinking of your
application as a data management process. Everything in your application
is about how you create, retreive, update, or delete data (CRUD).

Inputs matter

If the way you store data is critical, then just as critical is the way you
acquire it. In a web app, much of the time, that's going to be done
through the venerable HTML form.

Defining Forms

Forms are critical. They control what information we take in, and when we
validate our data, we validate what's come through our forms. In fact,
forms are so critical we don't want them to be treated as second-class
elements of our application; they are key components that deserve focused
attention and first-class status.

Now, for the most part forms we use in one interface are unlikely to be used
others (when was the last time you filled out a form in a CLI?). For that
reason, we're going to create a namespace in which to keep our forms; a
dedicated library wing just for web forms. All forms will go into this
namespace, and no forms will be created without them. Remember: forms are
first-class members of our application; they are the user's interface to
our database, and our interface to the users.

We'll keep our forms in Pizzaria::Web::Form. For example, if we want a form
to create/register a new user, it might be in Pizzaria::Web::Form::User::Register, or
if we want to update a user's profile, we could create a new form in
Pizzaria::Web::Form::User::Update.

$ mkdir -p ./lib/Pizzaria/Web/Form

With a little forethought, we can definitely save ourselves a lot of
repetition. We already said that the two main functions we want users to
be able to do is register as users and modify their profiles. It seems
very likely that both forms will have a lot of fields in common, and
that we'll want the same restraints on fields shared by both forms. So
how do we usually do that? Subclassing. We're going to use the same
concept here. Let's create a form object for Pizzaria::Web::Form::User,
Pizzaria::Web::Form::User::Register, and Pizzaria::Web::Form::User::Update,
in which our core functionality is described in the first, and the latter
two contain only overriding modifications to the base class.

Great. Next, let's create our base class in Pizzaria::Web::Form::User.
Before you go any further, read this excellent introduction to
HTML::FormHandler (http://www.catalyzed.org/2010/03/an-introduction-to-formhandler.html)
if you haven't already. We're lifting code from there en masse and life
will be easier for you if you understand what's covered there =)

Notice that we've omitted the "submit" field. That's because this base class
is never going to be used directly; it's just a foundation for our other user
form classes. Now we create our registration form:

That was easy. We just added a submit button and ... we're done. The rest
of the fields were inherited from the base class. Now, when we want to create
the registration form, we just create an instance of this class and
render/validate/whatever it. Let's make our user update form as well.

What about jQuery? (a.k.a.: Plugins!)

Ok, good for us; we can create forms easily ... but how about integration with
jQuery?

Great question. Let's look at some ways we can spiffy-up our forms with jQuery.
As we mentioned before, when you think jQuery, you should first think: Plugins!

jQuery's plugin community is like a decentralized CPAN. If you want to do it,
there's probably a plugin for it. If you want to do it differently, leverage
existing code to work for you.

Let's take a look at some particular plugins.

Unobtrusive AJAX with the ajaxForm plugin

If you're like me, you want lots of things to be happening with AJAX. It lets
you take advantage of the REST aspects of your app, lets you fire off asynchronous
events, looks cool, and brings about world peace. But how can we use forms
rendered through HTML::FormHandler and the Template Toolkit (http://template-toolkit.org/)
to make AJAX calls?

That's it =) Now you can convert any form on your page to an AJAX form
in a couple of lines of code. For extra credit, you can use this to
degrade gracefully in the event that the user has JavaScript disabled:
the form still loads, but works as a regular cgi-style page submission!

jQuery.ajax() for REST

So, you're big into REST, eh? No problem =) As you may know, not all
browsers handle DELETE and PUT requests (and jQuery doesn't provide extra
sugar for them). With a tiny bit of footwork, though, we can side-step the
issue.

jQuery lets you submit forms via GET, POST, PUT, and DELETE. Sugar is
provided for GET and POST, but I don't use them to keep things uniform
with my PUT and DELETE request. Let's PUT a user from our registration
form.

Yep; that's it. Now your page's form elements will tab in order of rendering
on the page. Of course, you could make it more complex -- I'll leave it as an
exercise to the reader to figure out how to derange his own forms.

Datepicker (jQuery UI core plugin)

What if we want our user to register his birthday as well, so we can send him
a coupon for free pizza? Easy. Of course, go add the right fields to your
form and the DB. After that, we'll use jQuery UI's core plugin Datepicker
(http://jqueryui.com/demos/datepicker/) to do the heavy lifting.

Let's assume that your form input has the attribute class="date". We'll
just attach the date-picker plugin to everything that's an input tag with that class:

All done =) It has sensible defaults to let you navigate the calendar with
the keyboard, supports localization, allows for hooking, and vast arrays of
customization. Check out the demo at (http://jqueryui.com/demos/datepicker/)
for more information.

Complex Layouts with UI.Layout

"Oh! Oh!" you pine, "I need a complex layout with many panes and fancy things!
Alas and alack, now I am lost!"

But no! Check out the venerable UI.Layout plugin (http://layout.jquery-dev.net/)
for jQuery. It can help you get a multi-paned, collapsible, spectacular
layout with relatively little markup. It's a little too involved to go
into in this article, but look at the demos and you'll see everything you
need to get started. It turns out it plays nicely with Catalyst and you
can do magic things with the stash to keep your views simple, your
controllers light, and your users happy.

Exercises for the Reader

This has been a very light introduction to using jQuery with Catalyst
applications. The goal here is to get you past the hurdles of getting
started with jQuery. With a bit of imagination, you can do some really
neat things; and the interplay between Catalyst and jQuery can be really
productive.

Every time you find yourself violating one of those boundaries, spend the
extra ten minutes or two hours to find the root of the problem and solve it
correctly, and you'll find that over time your development and debugging
time and costs drop, and most of your development time is on enhancements.