Looping¶
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:
- The result of
iterate()
needs to be buffered until therender_name
method is done.repeat()
is not buffered. Thus for huge loopsrepeat()
is the preferred method. - If you’re using
iterate()
, you should handle all child nodes within the loop, as they are marked as “done”, i.e. no render methods are called for them.repeat()
has no such effect.
repeat()
also fills the ctx
attribute of the node (and
all of its child nodes), which is a tuple containing:
- The number of the repetition (starting with zero)
- The current item
- The custom parameter tuple (the “fixed” argument in the
→ repeat()
function signature.)
Looping With Separators¶
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:
- define a
separate_name
method, which will be called automatically if a separator is needed. - Pass a callback function to the
→ iterate()
or→ repeat()
methods.
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>