Looping is basically about repeating a template block and filling each repeated item differently. There are two ways of looping: → node.iterate() and → node.repeat(). Looping with → node.iterate() looks like this:
from tdi import html
template = html.from_string("""
<html>
<body>
<ul>
<li tdi="menu"><a tdi="link">some menu item</a></li>
</ul>
</body>
</html>
""")
class Model(object):
def __init__(self, menu, page):
self._menu = menu
self._page = page
def render_menu(self, node):
items = enumerate(node.iterate(self._menu))
for idx, (subnode, (href, menuitem)) in items:
subnode.link.content = menuitem
if (idx + 1 == self._page):
subnode.link.hiddenelement = True
else:
subnode.link['href'] = href
menu = [
(u'/some/', u'Some Menu Item'),
(u'/other/', u'Editing Content & Attributes'),
(u'/third.html', u'Another Menu Item'),
]
model = Model(menu=menu, page=2)
template.render(model)
The → repeat() method uses a callback function instead of the loop body for the logic:
def render_menu(self, node):
node.repeat(self.repeat_menu, self._menu)
def repeat_menu(self, node, (href, menuitem)):
node.link.content = menuitem
if (node.ctx[0] + 1 == self._page):
node.link.hiddenelement = True
else:
node.link['href'] = href
The output is the same:
<html>
<body>
<ul>
<li><a href="/some/">Some Menu Item</a></li><li>Editing Content & Attributes</li><li><a href="/third.html">Another Menu Item</a></li>
</ul>
</body>
</html>
The main differences between those methods are:
repeat() also fills the ctx attribute of the node (and all of its child nodes), which is a tuple containing:
It’s often needed to also put blocks between repeated items. TDI calls these blocks separators. A common case is to just put a static piece of template code between, without doing anything special with the separator block itself.
<html>
<body>
<ul>
<li tdi="menu"><a tdi="link">some menu item</a></li><li tdi=":-menu">
</li>
</ul>
</body>
</html>
The separator node has to be defined on the same level as the (potential) list item node. It has to get the same name, but is decorated with the : flag. Separator nodes are special, they are attached to the accompanying “real” node and are used only if this “real” node is repeated (with more than one resulting item). Otherwise the separators just vanish from the output.
In the case above the separator simply adds some space characters, since the element is hidden (because of the - flag). The rest of the example (the python code) is stolen from the looping example above. The output is, of course, different:
<html>
<body>
<ul>
<li><a href="/some/">Some Menu Item</a></li>
<li>Editing Content & Attributes</li>
<li><a href="/third.html">Another Menu Item</a></li>
</ul>
</body>
</html>
If you want to modify separator nodes you can:
Inside a separator function you can access the context information (node.ctx) as well. In this case the current item is actually a tuple containing both the last and the next iterated item.
Here’s an example modifying the last separator:
from tdi import html
template = html.from_string("""
<html>
<body>
<p>My fruit salad contains <tdi tdi="-fruit">Apples</tdi><tdi
tdi=":-fruit">, </tdi>.</p>
</body>
</html>
""".lstrip())
class Model(object):
def __init__(self, fruits):
self._fruits = fruits
def render_fruit(self, node):
node.repeat(self.repeat_fruit, self._fruits, len(fruits) - 2)
def repeat_fruit(self, node, fruit, last_sep_idx):
node.content = fruit
def separate_fruit(self, node, last_sep_idx):
if node.ctx[0] == last_sep_idx:
node.content = u' and '
fruits = [
u'apples', u'pears', u'bananas', u'pineapples',
]
model = Model(fruits)
template.render(model)
<html>
<body>
<p>My fruit salad contains apples, pears, bananas and pineapples.</p>
</body>
</html>