Catalyst Advent - Day 18 - Catalyst::View::TT

One of the first things you probably want to do when starting a new Catalyst application is set up your View. Catalyst doesn't care how you display your data; you can choose to generate HTML, PDF files, or plain text if you wanted.

Most Catalyst applications use a template system to generate their HTML, and though there are several template systems available, Template Toolkit is probably the most popular.

Once again, the Catalyst developers have done all the hard work, and made things easy for the rest of us. Catalyst::View::TT provides the interface to Template Toolkit, and provides Helpers which let us set it up that much more easily.

Creating your View

Catalyst::View::TT provides two different helpers for use to use: TT and TTSite.

TT

Create a basic Template Toolkit View using the provided helper script:

    script/myapp_create.pl view MyView TT

This will create lib/MyApp/View/MyView.pm, which is going to be pretty empty to start. However, it sets everything up that you need to get started. You can now define which template you want and forward to your view. For instance:

    sub hello : Local {
        my ( $self, $c ) = @_;

        $c->stash->{template} = 'hello.tt';

        $c->forward( $c->view('MyView') );
    }

In most cases, you will put the $c->forward into end(), and then you would only have to define which template you want to use. The DefaultEnd plugin discussed on Day 8 is also commonly used.

TTSite

Although the TT helper does create a functional, working view, you may find yourself having to create the same template files and changing the same options every time you create a new application. The TTSite helper saves us even more time by creating the basic templates and setting some common options for us.

Once again, you can use the helper script:

    script/myapp_create.pl view myView TTSite

This time, the helper sets several options for us in the generated View.

    __PACKAGE__->config({
        CATALYST_VAR => 'Catalyst',
        INCLUDE_PATH => [
            MyApp->path_to( 'root', 'src' ),
            MyApp->path_to( 'root', 'lib' )
        ],
        PRE_PROCESS  => 'config/main',
        WRAPPER      => 'site/wrapper',
        ERROR        => 'error.tt2',
        TIMER        => 0
    });

INCLUDE_PATH defines the directories that Template Toolkit should search for the template files.
PRE_PROCESS is used to process configuration options which are common to every template file.
WRAPPER is a file which is processed with each template, usually used to easily provide a common header and footer for every page.

In addition to setting these options, the TTSite helper also created the template and config files for us! In the 'root' directory, you'll notice two new directories: src and lib.

Several configuration files in root/lib/config are called by PRE_PROCESS.

The files in root/lib/site are the site-wide templates, called by WRAPPER, and display the html framework, control the layout, and provide the templates for the header and footer of your page. Using the template organization provided makes it much easier to standardize pages and make changes when they are (inevitably) needed.

The template files that you will create for your application will go into root/src, and you don't need to worry about putting the the <html> or <head> sections; just put in the content. The WRAPPER will the rest of the page around your template for you.

$c->stash

Of course, having the template system include the header and footer for you isn't all that we want our templates to do. We need to be able to put data into our templates, and have it appear where and how we want it, right? That's where the stash comes in.

In our controllers, we can add data to the stash, and then access it from the template. For instance:

    sub hello : Local {
        my ( $self, $c ) = @_;

        $c->stash->{name} = 'Adam';

        $c->stash->{template} = 'hello.tt';

        $c->forward( $c->view('MyView') );
    }

Then, in hello.tt:

    <strong>Hello, [% name %]!</strong>

When you view this page, it will display "Hello, Adam!"

All of the information in your stash is available, by its name/key, in your templates. And your data doesn't have to be plain, old, boring scalars. You can pass array references and hash references, too.

In your controller:

    sub hello : Local {
        my ( $self, $c ) = @_;

        $c->stash->{names} = [ 'Adam', 'Dave', 'John' ];

        $c->stash->{template} = 'hello.tt';

        $c->forward( $c->view('MyView') );
    }

In hello.tt:

    [% FOREACH name IN names %]
        <strong>Hello, [% name %]!</strong><br />
    [% END %]

This allowed us to loop through each item in the arrayref, and display a line for each name that we have.

This is the most basic usage, but Template Toolkit is quite powerful, and allows you to truly keep your presentation logic separate from the rest of your application.

$c->uri_for()

One of my favorite things about Catalyst is the ability to move an application around without having to worry that everything is going to break. One of the areas that used to be a problem was with the http links in your template files. For example, suppose you have an application installed at http://www.domain.com/Calendar. The links point to "/Calendar", "/Calendar/2005", "/Calendar/2005/10", etc. If you move the application to be at http://www.mydomain.com/Tools/Calendar, then all of those links will suddenly break.

That's where $c->uri_for() comes in. This function will merge its parameters with either the base location for the app, or its current namespace. Let's take a look at a couple of examples.

In your template, you can use the following:

    <a href="[% c.uri_for('/login') %]">Login Here</a>

Although the parameter starts with a forward slash, this is relative to the application root, not the webserver root. This is important to remember. So, if your application is installed at http://www.domain.com/Calendar, then the link would be http://www.mydomain.com/Calendar/Login. If you move your application to a different domain or path, then that link will still be correct.

Likewise,

    <a href="[% c.uri_for('2005','10', '24') %]">October, 24 2005</a>

The first parameter does NOT have a forward slash, and so it will be relative to the current namespace. If the application is installed at http://www.domain.com/Calendar. and if the template is called from MyApp::Controller::Display, then the link would become http://www.domain.com/Calendar/Display/2005/10/24.

Once again, this allows you to move your application around without having to worry about broken links. But there's something else, as well. Since the links are generated by uri_for, you can use the same template file by several different controllers, and each controller will get the links that its supposed to. Since we believe in Don't Repeat Yourself, this is particularly helpful if you have common elements in your site that you want to keep in one file.

Further Reading:

https://metacpan.org/module/Catalyst

https://metacpan.org/module/Catalyst::View::TT

https://metacpan.org/module/Template

--Adam Herzog