Usually there’s more than one template in a project and there are certainly the same or similar elements on the different pages. In order to avoid code duplication, move the common elements into an extra file and load them where they are needed. TDI provides the concept of overlays for this purpose:
Especially the last item gives more flexibility than common include or inheritance mechanisms, because the final templates are explicitly picked at runtime (and not at template definition time). This allows for theming, localization, widgets and probably more use cases all at once.
As this may still sound complicated, here’s a picture:
Basic Overlay Mechanism
Note that the figure is simplified:
Here’s some example code:
from tdi import html
content = html.from_string("""
<html>
<body>
<h1 tdi="doctitle">doc title goes here</h1>
<ul tdi:overlay="menu"></ul>
<p tdi="intro" class="edit-intro">Intro goes here.</p>
<div class="list" tdi="list">
...
</div>
</body>
</html>
""")
menu_widget = html.from_string("""
<html>
<body>
<ul tdi:overlay="menu">
<li tdi="menu"><a tdi="link">some menu item</a></li><li tdi=":-menu">
</li>
</ul>
</body>
</html>
""")
class Model(object):
def __init__(self, menu, page):
self._menu = menu
self._page = page
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
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 = content.overlay(menu_widget)
template.render(model)
The code deals with three different templates objects:
If it’s clear whether a tdi:overlay node is an overlay source or a target, it’s recommended to flag them as such:
Flagging properly is generally recommended, because the overlay result is likely to be part of a bigger overlay chain (like t1.overlay(t2).overlay(t3) ...). If you flag the nodes, there are no ambiguities about the purpose of the particular node.
Note
If your editor doesn’t like the < and > characters inside an HTML attribute, you can write them as < and > as well.
The example code emits the following:
<html>
<body>
<h1>doc title goes here</h1>
<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>
<p class="edit-intro">Intro goes here.</p>
<div class="list">
...
</div>
</body>
</html>
By default a source overlay replaces the target node completely. But there are exceptions: If the target overlay specifies tdi or tdi:scope attributes, these are kept and override the ones specified in the source overlay.
While this probably doesn’t make much sense for navigational elements like the menu widget above, it has been proven useful for widgets included more than once in a page, because it allows to put the overlay into a different context.
The following (meaningless) example shows the principle:
from tdi import html
content = html.from_string("""
<html>
<body>
<h1 tdi="doctitle">doc title goes here</h1>
<ul tdi:overlay="menu" tdi="mymenu"></ul>
<p tdi="intro" class="edit-intro">Intro goes here.</p>
<div class="list" tdi="list">
...
</div>
</body>
</html>
""")
menu_widget = html.from_string("""
<html>
<body>
<ul tdi:overlay="menu" tdi="menucontainer">
<li tdi="menu"><a tdi="link">some menu item</a></li><li tdi=":-menu">
</li>
</ul>
</body>
</html>
""")
class Model(object):
def render_menucontainer(self, node):
print "menucontainer"
def render_mymenu(self, node):
print "mymenu"
model = Model()
template = content.overlay(menu_widget)
template.render_string(model)
And that’s the output:
mymenu