This isn't the best written tutorial, nor is it 100% inclusive or 100%
correct.  However, it worked well for me when introducing Template-Toolkit
as part of our site generation process.  

++t


On Sun, 15 Jul 2001, Stuart Johnston wrote:

> I am designing a web-based system using the Template Toolkit and I'm 
> having trouble coming up with a good way to explain to the user how to 
> write template files to interact with my system, describing file names, 
> variables and so forth.
> 
> Maybe you have some advice or could point me to some examples.
> 
> 
> Thanks,
> 
> Stuart Johnston
> http://bookex.sourceforge.net
> 
> 
> _______________________________________________
> templates mailing list
> [EMAIL PROTECTED]
> http://www.template-toolkit.org/mailman/listinfo/templates
> 

Tony Payne  :  Sr. Software Engineer  :  PETsMART.com  :  626-817-7151
Title: Sitegen Templating Tutorial

Sitegen Templating Tutorial

Simple Templates

Let's start with the world's simplest template:

Hello, World!

When the template above is processed the output would be:

Hello, World!

But, of couse, if that were all we needed to do, we would use static pages. So the template system allows substitution of variable values into the template:

Variable Substitution

Hello, [% name %]!

Now, when the template is processed, the variable name must be provided to the template. If name = 'Tony', the output would be:

Hello, Tony!

A template can use any number of variables:

Hello, [% first_name %] [% last_name %]!

We haven't seen you in [% time_since_last_visit %] days.

Is your address below still correct? 
  [% first_name %] [% last_name %]
  [% address1 %]
  [% address2 %]
  [% city %] [% state %], [% zip %]

Given the following variables,

  • first_name = Tony
  • last_name = Payne
  • address1 = 35 Hugus Alley
  • address2 =
  • city = Pasadena
  • state = CA
  • zip = 91103
  • time_since_last_visit = 5

the output will be:

Hello, Tony Payne!

We haven't seen you in 5 days.

Is your address below still correct?
  Tony Payne
  35 Hugus Alley
  
  Pasadena CA, 91103

Conditional Processing

What if you don't want the blank line when address2 is empty?

Hello, [% first_name %] [% last_name %]!

We haven't seen you in [% time_since_last_visit %] days.

Is your address below still correct? 
  [% first_name %] [% last_name %]
  [% address1 %]
  [%- IF address2 %]
  [% address2 %]
  [%- END %]
  [% city %] [% state %], [% zip %]

Gives the output:

Hello, Tony Payne!

We haven't seen you in 5 days.

Is your address below still correct?
  Tony Payne
  35 Hugus Alley
  Pasadena CA, 91103

If address2 = Suite 210, instead of being empty, then you'd get:

Hello, Tony Payne!

We haven't seen you in 5 days.

Is your address below still correct?
  Tony Payne
  35 Hugus Alley
  Suite 210
  Pasadena CA, 91103

Now, how did we do that?

We changed

  [% address2 %]
to
  [%- IF address2 %]
  [% address2 %]
  [%- END %]

IF is called a template directive and looks like:

  [% IF <expression> %]
    <template>
  [% END %]

The IF directive evaluates the expression and if the expression is "true", then the part of the template between the IF directive and the following [% END %] directive gets processed. If the expression evaluates to "false", then the template inside the IF block is ignored.

The simplest expression is simply a variable, as in our address2 example above. We'll see some more complicated expressions later.

The meaning of "true" and "false" depend on whether the expression (in our case, the variable) is a number or a string. If the expression evaluates to a number, then it is "true" if it is not equal to zero. If the expression is a string, then it is true if it is not the empty string.

You may have noticed that the new template looks like [%- IF address2 %], not [% IF address2 %] (it has a minus sign after [%). This is not specific to the IF and END directives. A minus sign after [% tells the template system to ignore the extra whitespace before [%. A minus sign before %] tells the template system to ignore whitespace after the directive. If we had left out the two minus signs above, then the output would have been:

Hello, Tony Payne!

We haven't seen you in 5 days.

Is your address below still correct?
  Tony Payne
  35 Hugus Alley


  Pasadena CA, 91103

Which would have only made our problem worse.

Now, let's say we need to do something special for residents of California:

Hello, [% first_name %] [% last_name %]!

[%- IF state == 'CA' %]
Did you know that PETsMART.com is in California, too?
[%- ELSE %]
Why are you in [% state %]?  You know, California is the place you ought to be!
[%- END %]

Hello, Tony Payne!

Did you know that PETsMART.com is in California, too?

But, if state = NY:

Hello, Tony Payne!

Why are you in NY?  You know, California is the place you ought to be!

So, if you want to have different output based on the value of a variable, you can use an IF, ELSE structure like the one above. What if you have several different cases?

Hello, [% first_name %] [% last_name %]!

[%- IF state == 'CA' %]
Did you know that PETsMART.com is in California, too!
[%- ELSIF state == 'TX' %]
Texas, our Texas, all hail the might state!  Yes, there is a Pasadena, TX.
[%- ELSE %]
Why are you in [% state %]?  You know, California is the place you ought to be!
[%- END %]

The above template adds yet additional special handling for cases where the tate is Texas.

Only one of the template blocks above will be executed. Take the following example:

[% IF state == 'CA' %]
...
[% ELSIF city == 'Pasadena' %]
...
[% END %]

If state = 'CA' and city = 'Pasadena', only the first block will be processed. ELSIF is only evaluated if the previous IF or ELSIF expression(s) was(were) false. ELSE is only evaluated if all IF and ELSIF blocks evaluated to false. There can only be one ELSE per IF.

Expressions

Let's take a look now at some more complex expressions:

[% IF state == 'CA' OR state == 'TX' %]

[% IF state == 'CA' AND city == 'Pasadena' %]

[% IF tax_rate > 1 %]
Where is this, Sweden?
[% END %]

[% IF first_name == last_name %]
Hey, you have a funny name!
[% END %]

[% IF first_name != 'Tony' %]
Good, I was afraid it was Tony, again!
[% END %]

[% IF not first_name == 'Tony' %]
Another way to say it.
[% END %]

[% IF x < 1 %]

[% IF x <= 1 %]

[% IF x > 1 %]

[% IF x >= 1 %]

Here is the full list of the "operators" that can be used as part of a template expression: == != < <= > >= ! && || and or not

You can also use parentheses to group sub-expressions together like so:

[% IF ( x < 1 and y > 2 ) or ( x > 2 and y < 1 ) %]

Which is very different from

[% IF x < 1 and ( y > 2  or  x > 2 ) and y < 1  %]

Loops

There are a lot of times when you'll want to process a list of things. The templating system supports this through various loop constructs, only one of which we'll describe in detail here.

Let's start with an example. Say you want to make a dropdown with a list of states.

<select name="state">
  <option value="">Choose a state
[% FOREACH state_obj = states %]
  <option value="[% state_obj.abbr %]" [% IF state == state_obj.abbr %]SELECTED[% END %]>[% state_obj.name %]
[% END %]
</select>

In this example, we assume that the template has been provided with a list of state "objects" called states. The FOREACH directive will process it's block once for each item in the list, assigning the value of the current element in the list to the variable (state_obj) specified.

The elements of the states list are referred to as objects because they are not simple variables. Unlike the state variable, which in the previous examples held a 2 letter state abbreviation, these objects have both an abbreviation (state_obj.abbr) and full name (state_obj.name). Object variables can have multiple "attributes", while simple variables can only have one value.

Anyway, back to talking about loops... Here is the structure of the FOREACH directive:

[% FOREACH <variable_name> = <list> %]
<template>
[% END %]

Like the <expression> in the IF directive, there are a lot of different ways to specify a list. The most common, however, is by specifying a variable which you know is a list variable. Another possible method is by specifying an attribute of an object which is a list:

[% FOREACH subsection = section.subsections %]
<a href="[% subsection.url %]">[% subsection.section_title %]</a><br>
[% END %]

In this example, section is an object variable and has an attribute named subsections which returns a list of section objects (possibly including the original section object too!). The existing code might be used, for example, to generate the left-hand spine of a shopping page.

Variable Types

Since we've now introduced three different variable types, it's time to clarify the concept and give a full explanation of the different types.

Simple

Simple variables are single-valued. They may contain a string like Tony or a number. Simple variables can be directly substituted into a template using [% <variable_name> %]

There are a number of special "methods" which can be invoked on simple variables. E.g.:

[% IF not state.defined %]
You have not specified a state.
[% END %]

  • defined: Returns a true value if the variable has been defined.
  • length: Returns the number of characters in a string.
  • repeat(n): Returns the string, repeated n times.
  • replace(search, replace): Returns the string with all instances of search replaced by replace.
  • search(pattern): Returns true if the string matches pattern pattern
  • split(pattern): Splits the string into a list of strings, breaking the string at each instance of pattern.

List

List variables contain a sequence of other variables. The elements of the list can be simple variables, objects, other lists... any type of variable. Lists can be looped through using FOREACH directives. You can also access a specific element in a list by specifying the element's index. Indexes for a list start from zero, so [% states.0.abbr %] will substitute the abbreviation for the first element (AL, if in alphabetical order).

There are a number of special "methods" which can be invoked on list variables. E.g.:

There are [% states.size %] states in the list.

In this example, the size method is called on the states list. The size method returns the number of elements in the list.

Here is the full list of methods:

  • first: Same as list.0. Returns the first element of the list.
  • last: Returns the last element of the list.
  • size: Returns the number of elements in the list.
  • max: Returns the maximum index in the list. This is always equal to size - 1.
  • reverse: Returns the elements of this list, in reverse order.
  • join(separator): Can be used if all of the elements of the list are simple variables. join will return a simple variable which has all of the elements of the list separated by the separator argumrent. E.g.: [% state_abbreviations.join('<br>') %]
  • sort: Returns the elements of the list, sorted alphabetically. Works only if all the variables are simple variables.
  • nsort: Returns the elements of the list, sorted numerically. Works only if all the variables are simple variables.
  • unshift, push, shift, pop: These methods alter the list, so are not explained here.

Associative List

Associative List variables hold a list of name, value pairs. Values may be looked up based on name. A given name may have either zero or one values. Say you have a states associative list, where the keys (the names) are abbreviations and the values are the names. You might see code like the following:

CA stands for [% states.CA %]

As you see in the above example, you get the value for a given key by using <assoc_list>.<key>. Sometimes, though you have the key value in a variable, like so:

You live in [% states.$state %]

The $state tells the template system that you want to retrieve the value from the states associative list for the key which is the value of the state variable. This process is called "interpolation". Here is a very common use of interpolation:

[% FOREACH abbr = states.keys.sort %]
[% abbr %] [% states.$abbr %]<br>
[% END %]

This example loops through all of the states, sorted by postal abbreviation, and prints the abbreviation followed by the full name of the state.

The keys method above is one of the special methods for associative list variables. Here is the full list:

  • keys: Returns a list of all of the keys, in random order.
  • values: Returns a list of all of the values, in the same random order as keys.
  • each: Returns a list of keys and values. Rarely useful.
  • sort: Returns a list of keys, sorted such that their respective values (simple variables only) are in alphabetical order. This deserves an example:

    [% FOREACH abbr = states.sort %]
    [% states.$abbr%] [% abbr %]<br>
    [% END %]
    

    Note that this example is sorted by the name of the state, not by the abbreviation.
  • nsort: Returns a list of keys, sorted such that their respective values are in numerical order.
  • import: Modifies the associative list, so will not be further discussed here.

Object

Object variables will vary greatly from each other, since they are defined, not by the template system, but by the system managing the template system. In our case, that's sitegen. Object variables can have named attributes, very similar to associative lists. More often than not, there will be a large number of named attributes. Objects can also have methods. Unlike the methods for simple, list and associative list variables, there is no set list of methods for objects. Each object type defines its own methods. The object types defined by sitegen, which are described in detail in data_api.html, are:

Includes

There are three different ways to process includes:

[% INCLUDE template var1 = value var2 = value%]
[% PROCESS template %]
[% INSERT template %]

INCLUDE and PROCESS cause another template, or a sub-portion of the current template to be processed and included in the current location.

<select name="state">
[% FOREACH state = states %]
[% INCLUDE state_row %]
[% END %]
</select>

[% BLOCK state_row %]
[% state.abbr %] [% state.name %]<br>
[% END %]

BLOCK defines a sub-template which can be included at any point within its parent template. It inherits any variables which are present at the time of include, such as the state variable in the example above. You can also include full templates, defined outside of the current template: [% INCLUDE header %] will allow a header template that lives outside of all other templates, so that all pages use a common header format, without having to copy-and-paste the header code all over.

The only difference between INCLUDE and PROCESS is that if you modify a variable which is INCLUDEed, the changes are temporary, because a copy is made of all of the variables before the sub-template is processed. PROCESS uses the same copy of the variables as its parent, so any changes made get propogated back to the parent. For both, you can pass variables directly to the sub-template by specifying <varname> = <value> after the directive. Normally, you would do this if the sub-template calls for a variable with a different name than one you have available.

INSERT does no processing at all on the included file. If there are variable substitutions in the file, they are not processed. You would instead see the [% ... %] in the outputted file. INSERT should be used when including static content (like tiles) into a page. For this, it is faster than INCLUDE or PROCESS because it does not need to search the include for directives.

Reply via email to