|
Synopsis
The Discus Template Language includes iteration through an array, performing operations and displaying items for each element of the array. Each element of an array passed to a template is itself a hash (in Perl, the array is an array of hash references). In addition to the specific keys of that hash that are defined by the user, the template language itself defines certain special keys to make manipulation of the elements extremely easy within the template.
For many examples on this page, we will discuss a hypothetical array named @rainbow, which will describe the colors of the rainbow. The elements of our array will be hashes, with the key "color" containing the English name of the color, and "hexcode" containing the appropriate hex code for the color.
| color |
hexcode |
| Red |
ff0000 |
| Orange |
ff8800 |
| Yellow |
ffff00 |
| Green |
00ff00 |
| Blue |
0000ff |
| Indigo |
8000ff |
| Violet |
ff00ff |
General Structure
Each FOREACH loop begins with <#foreach $variablename (@arrayname)#> on a line by itself, and ends with <#endloop#> on a line by itself. The statements between the beginning and end of the loop are executed once for each element of the array, with the variable $variablename being set equal to the element of the array that is being processed (much like the Perl foreach statement).
Here is one possible FOREACH loop that iterates over the colors of the rainbow.
<#foreach $element (@rainbow)#>
Your array contains an element
<#endloop#>
Your array contains an element
Your array contains an element
Your array contains an element
Your array contains an element
Your array contains an element
Your array contains an element
Your array contains an element
The template language prints "Your array contains an element" each time it encounters an element of the array (your array has 7 elements so this text is repeated 7 times). The FOREACH syntax is much more useful when you actually make use of the data that's stored in the array.
<#foreach $element (@rainbow)#>
$element->{color}
<#endloop#>
Red
Orange
Yellow
Green
Blue
Indigo
Violet
You can use more than one key within each statement. Here is an example of a useful way that your array could be output.
<#foreach $element (@rainbow)#>
<font color="#$element->{hexcode}">$element->{color} has the color $element->{hexcode}
<#endloop#>
Red has the color ff0000
Orange has the color ff8800
Yellow has the color ffff00
Green has the color 00ff00
Blue has the color 0000ff
Indigo has the color 8000ff
Violet has the color ff00ff
Helpful Run-time Definitions
When iterating over an array, several hashes are defined for each element of the array that can assist you, especially in writing conditional code. The following table lists the hashes that are defined automatically for each element of the array.
| Key |
Description |
| _iteration |
Iteration counter, starting from 1 |
| _iteration_minus1 |
Iteration counter, starting from 0 |
| _is_last_element |
1 if this is the last element in the array, 0 otherwise |
| _is_first_element |
1 if this is the first element in the array, 0 otherwise |
| _internal_counter |
Counter of all iterations, starting from 1 |
Here is an example of how you might use these run-time definitions, by putting every other result in italics and the first and last entries in bold.
<#foreach $element (@rainbow)#>
{#if $element->{_is_first_element} || $element->{_is_last_element}#}<b> \
{#if [ $element->{_iteration} % 2 ] == 0#}<i> \
<font color="#$element->{hexcode}">$element->{color} has the color
$element->{hexcode}
{#if [ $element->{_iteration} % 2 ] == 0#}</i> \
{#if $element->{_is_first_element} || $element->{_is_last_element}#}</b>
<#endloop#>
Red has the color ff0000
Orange has the color ff8800
Yellow has the color ffff00
Green has the color 00ff00
Blue has the color 0000ff
Indigo has the color 8000ff
Violet has the color ff00ff
Skipping elements and iterations
At times, it may be necessary to skip an element or an iteration. The following codes take the described actions:
| Code |
Description |
| <#next#> |
Skips all code between <#next#> and <#endloop#> (essentially skips processing directly ahead to the next element of the array) |
| <#last#> |
Skips all code between <#last#> and <#endloop#> and skips all code for subsequent elements (essentially stops processing of the array entirely at the point where it occurs) |
| <#skip iteration#> |
The iteration counters (_iteration and _iteration_minus1) are not incremented for this element |
<#foreach $element (@rainbow)#>
{#if $element->{color} =~ match(o)#}<#skip iteration#>
{#if $element->{color} =~ match(r)#}<#next#>
{#if $element->{color} =~ match(nd)#} Bye bye <#last#>
$element->{_iteration}: <font color="#$element->{hexcode}">$element->{color} has the color
$element->{hexcode}
<#endloop#>
2: Orange has the color ff8800
2: Yellow has the color ffff00
3: Blue has the color 0000ff
Bye bye
The above result is explained as follows. The word "red" does not contain "o" and thus the iteration counter is increased from 1 to 2 after processing. But "red" does contain "r" and thus everything from there to the <#endloop#> is skipped (nothing prints for "red"). The words "orange" and "yellow" both contain "o" so the iteration counter is not increased; however, these colors do not contain "r" or "nd" so they print normally. Green is skipped because "green" contains "r", but the iteration counter is increased from 2 to 3 because "green" does not contain "o". Blue is displayed normally, as it does not match any of the patterns. The word "indigo" contains "nd" so the words "Bye bye" are printed, and all further processing of the loop is ended due to the <#last#> statement.
Looking ahead and behind
Often times it is useful to know something about the previous or next element to help decide what to do with the current element. The Discus template language therefore defines the following special hashes:
| Hash |
Description |
| _previous_element |
The previous element (if you are currently on the first element, the first element is returned) |
| _next_element |
The next element (if you are currently on the last element, the last element is returned) |
This makes the following display possible:
<#foreach $element (@rainbow)#>
The previous color is $element->{_previous_element}->{color}.
My color is $element->{color}.
The next color is $element->{_next_element}->{color}.
<#endloop#>
The previous color is red.
My color is red.
The next color is orange.
The previous color is red.
My color is orange.
The next color is yellow.
The previous color is orange.
...
The previous color is indigo.
My color is violet.
The next color is violet.
It is left as an exercise to the reader to prevent the "previous color" statement from displaying for the first element, and to prevent the "next color" statement from displaying for the last element.
Nesting FOREACH statements
It is possible to nest one FOREACH statement within another, so long as the variable name you use for the iteration of each is different. For example, the following is valid syntax:
<#foreach $element1 (@rainbow)#>
Loop 1: $element1->{color}
<#foreach $element2 (@rainbow)#>
Loop 2: $element2->{color}
<#endloop#>
<#endloop#>
Loop 1: Red
Loop 2: Red
Loop 2: Orange
Loop 2: ...
Loop 2: Violet
Loop 1: Orange
Loop 2: Red
Loop 2: Orange
Loop 2: ...
Loop 2: Violet
Loop 1: Yellow
...
Creating an array you can iterate over
This advanced operation allows you to create an array you can iterate over. Suppose you are iterating over array @array1 (using variable $element). Suppose further that each element of your array contains an array within $element->{subarray}, and you want to iterate over that array. In Perl you could use @{ $element->{subarray} }. With the Discus Template Language, you cannot use references as arrays (this is a simple template processing language after all). However, you can define a new array from the array found in a variable as follows:
<#foreach $element (@array1)#>
<#reference array @array2 from $element->{subarray}#>
<#foreach $element2 (@array2)#>
...
<#endloop#>
<#endloop#>
The <#reference array ...#> statement must appear on a line of its own, and cannot be used within an in-line IF-THEN-ELSE statement. The definition of the array will hold throughout the remainder of processing of the template. You can define and re-define an array in this manner as many times as you need. While this is valid even outside a FOREACH loop, it is most valuable within a FOREACH loop. Note that the variable must be of the form $hash->{key}; other variable formats are not valid for this operation.
You can find an example of this construct with the listing of each topic a moderator is authorized to edit (admin/contact1.tmpl template).
|