The tal:repeat
attribute
A tal:repeat
attribute is used to repeat a subtree of the markup (an element and all of its content & descendents) for every item within an IEnumerable
collection.
Developers may think of this as being similar to a foreach
loop.
The syntax of a repeat attribute is comprised of:
- A variable name
- The TALES expression which is an
IEnumerable
collection
The variable name must follow the same rules as a tal:define
variable name, notably it must be a valid C# variable name.
Additionally, if the TALES expression does not evaluate to one of null, or an object which implements IEnumerable
, or an instance of
The element is duplicated for each item in the collection
The functionality of the tal:repeat
attribute will duplicate the entire subtree (starting with the element upon which the tal:repeat
attribute is present) once for every item in the collection.
All TAL attributes in that subtree are also duplicated and may thus be evaluated many times.
In each iteration, a local variable is created using the variable name specified in the tal:repeat
attribute.
That variable's value is set to the item from the current iteration of the collection.
The iterations and the variable values use the exact same order as which the IEnumerable
returns them.
Any inherently-ordered collections such as IList<T>
will have their order respected by repeat attribute.
All other TAL attributes on the element or its descendents (including those which have been duplicated) are handled as-normal as if the template source had originally been written with those repetitions hard-coded.
Beware of order-of-operations
As noted in the table listing all TAL attributes, the tal:repeat
attribute is processed after tal:define
attributes & tal:condition
attributes.
This creates a very easy-to-make mistake as shown in the following code snippet:
<ul tal:repeat="item here/someItems"
tal:define="itemName item/name">
<li tal:content="itemName">Item name</li>
</ul>
Can you see the mistake?
Even though the tal:repeat
attribute is written before the tal:define
attribute, it will be processed afterwards.
This means that the tal:define
attribute will attempt to evaluate the expression item/name
before the item
variable has been created, and will almost surely raise an error.
If the collection is empty
If the expression evaluates to an IEnumerable
which is empty (has no iterations) then the subtree beginning with the element which has the tal:repeat
attribute is removed from the rendered output entirely and is not processed.
In other words, no items means no repetitions of the markup.
If the collection is null
If the expression evaluates to a null reference, then the behaviour is exactly the same as if the action were aborted, as described below.
Aborting a tal:repeat
attribute
If the expression evaluates to an instance of AbortZptActionToken
, such as via the root context default
then the subtree beginning with the element which has the tal:repeat
attribute is left as-is.
Also, when a repeat attribute is aborted, no variable is defined for the current iteration.
Be aware that this could cause errors when ZPT processes TAL attributes upon descendent elements, if they attempt to make use of the 'current iteration' variable.
If the expression result is not IEnumerable
If the expression result is not IEnumerable
and is also neither null or an instance of AbortZptActionToken
then ZptSharp will raise an error.
Repeat variables
As well as a local variable, metadata about the current iteration is also accessible from a special root context named repeat
.
The contents of the repeat
root context are a collection of named objects corresponding to all of the 'current iteration' variables which are currently in-scope.
Each is named using the variable-name given in its corresponding tal:repeat
attribute.
Each of these 'repeat objects' provides the following properties:
Property | Meaning |
---|---|
index |
The zero-based index of the current iteration |
number |
The one-based index of the current iteration (equivalent to index + 1 ) |
even |
true if the index is an even number (0, 2, 4 etc), false otherwise |
odd |
true if the index is an odd number (1, 3, 5 etc), false otherwise |
start |
true if the index is zero, false otherwise |
end |
true if the current iteration is the last, false otherwise |
length |
The count of all items in the collection |
letter |
A string 'letter reference' for the current item: a, b, c, ... aa, ab, ac etc |
Letter |
The uppercase equivalent of letter |
roman |
A lowercase roman-numeral representation of number : i, ii, iii, iv etc |
Roman |
The uppercase equivalent of roman |
Examples
A table showing the roman numerals for 1 to 10
Let's presume that the model expression here/oneToTen
holds the result of Enumerable.Range(1, 10)
(using Enumerable
from the System.Linq
namespace).
The following example would create a table showing the decimal number on the first column and its uppercase roman numeral equivalent on the second column.
<table>
<thead>
<tr>
<th>Decimal</th>
<th>Roman</th>
</tr>
</thead>
<tbody>
<tr tal:repeat="number here/oneToTen">
<td tal:content="number">Decimal number</td>
<td tal:content="repeat/roman">Roman number</td>
</tr>
</tbody>
</table>
A list of items in a shopping cart
Let's presume that the model expression here/shoppingCartItems
holds a collection of objects which are instances of the following class.
public class ShoppingCartItem
{
public int Quantity { get; set; }
public string Name { get; set; }
public string ImageUrl { get; set; }
}
Here is some sample markup for a list of shopping cart items.
<ul>
<li tal:repeat="item here/shoppingCartItems">
<img src="images/placeholder.png" tal:attributes="src item/ImageUrl">
<span class="name" tal:content="item/Name">Item name</span>
<label for="itemQuantity_0"
tal:attributes="for string:itemQuantity_${repeat/item/number}">Quantity</label>
<input class="quantity"
id="itemQuantity_0"
value="0"
type="text"
tal:attributes="value item/Quantity;
id string:itemQuantity_${repeat/item/number}">
</li>
</ul>
This example brings together a number of techniques:
- The
src
attribute for the image is replaced using atal:attributes
attribute - The item name is filled-in using a
tal:content
attribute - The
<input>
element for the quantity is assigned a uniqueid
attribute by using the current repetition-number - The
<label>
element has itsfor
attribute set to the same value as theid
of the corresponding<input>
element