I’ve documented my quest for templating nirvana before (here, here, here and here… oh and also here), and thought I’d come to a reasonably satisfactory conclusion in some of my latter experiments.
My premise was that none of the templating engines currently available (you name the language/platform), come close to what I feel should be the fundamental goal of xml/xhtml templating — complete separation of markup from the code that populates it with dynamic data. There are a few templating systems that come close (Tapestry’s templating in the Java world and HTMLTemplate in Python, for example), but none that pushed all the buttons I thought needed to be pushed.
So, to this lofty ideal, I offered the idea of using a simple ID on elements and then putting all complexity in code to handle repeating of child elements, setting of attributes, setting node values, hiding elements, etc.
All in all it seemed to work for the (admittedly limited) cases I tried, but that’s the problem with grandiose schemes that are mainly based in the theoretical. Come time to apply the ideas in a practical sense, and you suddenly discover you haven’t thought it through well enough. Or to be more precise, I suddenly discovered I hadn’t thought it through well enough.
Case in point: I believe that in some situations it should be easy to use a different template without having to make modifications to the generating code. Which is where my simple IDs fall over in a quivering heap. Here’s an example (contrived) which immediately breaks it:
Example 1:
<users>
<user name="Joe Bloggs">
<address>10 Test St</address>
<city>somewheresville</city>
</user>
</user>
Example 2:
<html>
<body>
<ul>
<li>Joe Bloggs
<ul>
<li>10 Test St</li>
<li>somewheresville</li>
</ul>
</li>
</ul>
</body>
</html>
The xml in example 1 uses an attribute for the user’s name. The html in example 2 uses a textual element for the name, followed by another unorder list for the address elements. I could make the examples more complicated to show even more disparities, but you hopefully get the idea: if we’re just using an ID to distinguish elements, how do you say “set the attribute name to this value” for example 1 and “set the element value to this” for example 2, without having massively complicated code that either allows for both cases (and uses some kind of logic to figure out which should be applied), or has different code for each?
It’s naff. To put it politely. Not to mention that littering a template with unnecessary IDs is just as ugly as littering it with code.
So, in no way have I managed to reach templating nirvana. I have, however, demonstrated that if you’re not using this stuff in anger, there’s no way you’re going to come up with a good solution. Certainly some of the templating systems I’ve used (JSF springing immediately to mind) tend to suggest that whomever came up with the concepts didn’t follow through and use them either. So at least I’m in good company.
Enter the latest vain attempt.
My current thinking is that the template needs 3 ID attributes, one for repeating an element (rid), one for setting attributes (aid), and one for setting node values (eid). This gives a reasonable amount of flexibility and allows for the same code to populate different templates.
This code is very much a work in progress… and very much a completely inelegant mess. But it does satisfy a couple of requirements: 1) I am actually using it, and 2) the same code can be used to populate a variety of templates without requiring code changes.
It uses 4suite’s xpath to set values, and the use of xpath no doubt means performance will be adversely affected, but it’s a start.
In terms of the above examples, you might use this engine as follows:
<users rid="user-list">
<user aid="user-name" name="">
<address eid="user-address"></address>
<city eid="user-city"></city>
</user>
</user>
<html>
<body>
<ul>
<li rid="user-list" eid="user-name">
<ul>
<li eid="user-address"></li>
<li eid="user-city"></li>
</ul>
</li>
</ul>
</body>
</html>
tmp = Templates()['users.xml']
tmp.repeat('user-list', 2)
tmp.setelement('user-name', 'Joe Bloggs', 1)
tmp.setattribute('user-name', 'name', 'Joe Bloggs', 1)
tmp.setelement('user-address', '10 Test St', 1)
...etc
In the case of the first template, the attribute name is set with the value “Joe Bloggs”, and for the second template, the element value is used. The idea is, don’t use the relevant ID (rid, eid, aid) if you don’t need to apply that value.
More to come as I iron out the wrinkles…
Note: the template engine uses the Borg pattern.