First is Tapestry and Wicket. They gave me the idea of using standard HTML with minor extensions as my template language.
Second, is Web Forms and HTML5 as defined by the WHAT-WG.
In the Web Forms spec is described how to handle repeating objects
and form inputs in a clean and concise manner in HTML attributes. As I
considered it, I started to think about automatically filling in the
same information from the server side. That got me as far as using
<span awname="objname">
. Then as I moved forward, I realised
that this could be a very useful way of seperating the concerns of the designer
from the programmer. The designer would be able to design his pages using real
data on the page instead of $objname$
to represent the data. He'll
see what could actually show up!
The third influence is String Template. The author of that template engine explained why template engines should not have a complete Turing Machine available to the template writer. Indeed, doing so complicates things and makes it harder to create reusable templates. I was already thinking down these lines, and came to a similar realisation, but his paper (pdf) helped solidify the thoughts in my head. He says that the four things a template writer needs are:
Attribute Reference is obvious, and I had already completed it and conditional inclusion when I read Dr. Parr's paper. Multivalue application I was figuring out how to do in such a way that the template writer can use sample data, all the while creating well-formed and valid HTML. Recursive reference I struggled with, thinking that it would be more complicated to write templates for than I wanted, but have finally created it anyway just to be complete. It would actually be quite useful for including html from a database, since in all cases of attribute reference, the text is escaped to avoid accidentally allowing the programmer from inserting a bunch of html inside the attribute, or worse, someone trying to do cross site scripting. Of course, once I allow html from a database, that immediately opens up the cross site scripting problems again.
Anyway, Terence Parr (creator of StringTemplate) went on to describe some good seperation rules to keep the template and the business logic seperate. They are
Now, for exactly what all those things mean you can read his paper, but if we take those as good guidelines, we immediately see that template engines like Smarty don't make it easy to follow them. Indeed, with Smarty, it's very easy and tempting to do data value comparison and to include layout and display information in the model. I've done quite a bit of searching and haven't found very many template engines that don't make it extremely easy to do.
I have a few ideas of my own as to what should show up in a web template engine, including:
Now, other programmers out there might not agree with my assessment of what should be in a template engine. I can see the look of disdain on their faces when they're told they can't include html in their output as it will be escaped. This, of course, being on the face of programmers who aren't fanatical about the Model-View-Controller methodologies. Personally, I'm still exploring the whole Model-View-Controller methodologies myself. I have been for over 7 years. As a general principle to aim toward to make things easier to modify later seems good. I've never worked with a system that enforced the seperation as Ante does. I created Ante more as a way of trying this out than anything.
In a word: XHTML! (That's by a loose definition of
"word".) The engine is specifically for the web and I'm using standard and
validating html syntax. I've been doing some searching and haven't found many
other template engine that uses straight HTML as the template. And
those that try seem to use strange extensions like <jsp:include
page="pagename">
or worse <%@ include file="pagename"
%>
(Those are out of JavaServer Pages from
O'Reilly and Associates.) We may as well
just return to PHP with <?php include 'filename'; ?>
Historical note, yes, I know PHP and JSP are old. There are newer template engines, but I've noticed that they all either ignore HTML (like Smarty) or they extend it in weird ways like JSP. I like neither.
So, instead I use the standard HTML syntax. No extensions, and you can use sample data where you're going to have real data show up. I do so using the class attribute. The class attribute is generally considered to be good for specifying things with CSS. Whether you're describing that all elements with the class 'message' are in red, or whatever. And true, class seems to have been placed into the HTML spec for that purpose; however, it's not limited to only that. Any HTML processor (including a template engine) can use it. They say that you knew a software tool was a success when it was being used for something its creator had never dreamed of. I'm not sure if the creators of the class attribute dreamed of using the class attribute for templating or not, but to me the class attribute is a success.
So, look at this fragment of code: <span class="message
aw_msg">This is the template message</span>
.
You can see
that it's an HTML fragment of a span that has two classes associated
with it and some text in it. One of the classes is a normal CSS class
"message" and the template designer would use her favourite web site
editor to associate some CSS with that class. According to the
HTML spec, an element may have multiple classes associated with it,
seperated by spaces. The browser will apply CSS designs to the element
according to the classes that are referenced in the CSS and will ignore
any it doesn't know about. That's important, it means we can set
the class on the element without worrying about using up the class
attribute, such as if I had chosen to use the id attribute.
Now, when Ante processes the template, it recognizes
the aw_ prefix on one of the classnames and when the page is rendered,
Ante looks for a field in its data source by the name (without
the aw_ prefix) and replaces the text inside the element with it. So,
for this case, if the data source had a field 'msg' with the value
"Hello, World", the output would be: <span class="message
aw_msg">Hello, World</span>.
You'll notice that the class aw_msg is still on the span. This is intentional. It is so that I can do my #1 idea: "handle output data as an example with which to work". Someone could then take that output data and use it to create a new template (tweaking what's there for instance) and then return it to Ante as the template to work with.
My #3 idea is mainly to make things work properly when you come to
trying to deal with input elements. <input class="aw_firstname"
/> would be enough to produce the output <input
class="aw_firstname" name="firstname" value="Alan" />
assuming that
there was a field called firstname with my name as its value. The
template designer need neither know nor care what the input name is
going to be since it's not her job to know that. She just needs to know
the class name to set. If the name or value attributes are set, Ante
will overwrite them with the real values.
For more advanced use, such as outputting css on the fly (which, at my last job, we needed to do for various reasons), the template designer can set the class attribute on the style element. Yes, that's right. The HTML spec allows a class attribute on the style element. Normally you wouldn't, but we're still not breaking anything. The designer might have the CSS that doesn't change (the "normal" css) in a seperate file using link rel="stylesheet", or a style element, and then after that style element have one with a class="aw_varyingcss" and the programmer sets a field on the datasource with the variable css as its value (probably picked from a database). This can also work with the script tag!
For completeness, what about the case where the template designer
wants to put negative numbers red (for an accounting package for
instance)? First off, the programmer will have to give the ability to
decide if a number is negative by adding an attribute (or we'll break
seperation rules 2 through 4). Negativity is a business decision. So,
say the programmer creates a field negative, then she could use
conditional inclusion as described below, but that will break my idea
#1. So instead, the programmer creates a field sign which can be
negative, positive, zero. There's another special class prefix: ac_.
The designer could create a class in CSS
ac_sign_negative {color: red}
and then assign it to the element that outputs the number like so.
<td class="ac_sign_what aw_total">17.00</td>
which would
replace the word what with value of the field sign (negative etc.) and
the 17.00 with the value of the field total.
NEW Then there's setting other attributes, such as the href
of an anchor.
<a href="thisisvariable.html" class="aa_href_url">Click
Here</a>
. This is how to set the value of an attribute from
an Ante template. The syntax is straightforward
aa_{attribute name}_{datasource name}
Okay, that covers Dr. Parr's first rule: attribute reference.
Conditional inclusion is the second one. This is straightforward for
simple text such as our span above. <span class="message
aw_msg">This is the template message</span>
will end up
with an empty text since there's nothing to show if there is no field
called msg on the datasource. So, following that if we have
<span class="aw_showordont">This is the template text.<a
href="url2.com">link</a> message</span>
, we have a
conditional. If there's a field not equal to the value false in the
datasource, the contents of the element This is the template text.<a
href="url2.com">link</a> message will be shown. And there
could be a attribute reference or even a conditional which will be
treated as expected inside there as well. If there is no such field, or
it's equal false. The contents won't be shown.
Conditional inclusion means that we're wrapping an area with a name.
For instance, <span class="aw_employee">look at <a
class="aw_name" href="bob.html">Bob</a></span>
, we
have the field employee around the name, then we can have another span
<span class="aw_manager">look at <a class="aw_name"
href="john.html">John</a></span>
and we might expect
that the second time the name is referenced it is referring to the
manager's name instead of the employee's name. And that's exactly what
happens, provided the programmer passes in data that way. The
programmer can set up employee as a namespace (allowed by my #3 idea)
and manager as a namespace and assign a name attribute to each of them.
The two spans above would then refer to the correct name attributes.
If the template writer desired to refer to the manager's name
outside of a conditional inclusion element, she could write the
following <span class="aw_manager-name">John</span>
.
This could even be inside the employee span, since the rendering engine will
look for manager inside employee and not finding it, will look in the
outer namespace.
I'm going to skip Parr's rule 3 and come back to it. I'm instead
going to skip to his rule 4: template application to a multivalued
attribute. I built this on top of conditional inclusion and namespaces.
The template writer creates something similar to <ul>
and Ante
recognises that the friends attribute will be a multivalued
attribute that should then be looped over. The loop is implicit on the fact that the name of the attribute is an integer!
You may notice of course that using the namespaces described above,
that you don't really need to type friends each time, instead doing
this:
<li class="aw_friends-1">one</li>
<li class="aw_friends-2">two</li>
<li class="aw_friends-3">three</li>
</ul><ul class="aw_friends">
That's perfectly
acceptable too, although when you start getting into multivalued
attributes which have multivalued attributes as their values, it gets
confusing.
<li class="aw_1">one</li>
<li class="aw_2">two</li>
<li class="aw_3">three</li>
</ul>
Here's a messier example of that:
<table class="aw_table ">
<tr>
<th class="aw_header-1 ">col1</th>
<th class="aw_header-2 ">col2</th>
<th class="aw_header-3 ">col3</th>
<th class="aw_header-4 ">col4</th>
</tr>
<tr class="aw_1 ">
<td class="aw_1 ">data1</td>
<td class="aw_2 ">data2</td>
<td class="aw_3 ">data3</td>
<td class="aw_4 ">d3</td>
</tr>
<tr class="aw_2 ">
<td class="aw_1 ">data4</td>
<td class="aw_2 ">data5</td>
<td class="aw_3 ">data6</td>
<td class="aw_4 ">d6</td>
</tr>
<tr class="aw_3 ">
<td class="aw_1 ">data7</td>
<td class="aw_2 ">data8</td>
<td class="aw_3 ">data9</td>
<td class="aw_4 ">d9</td>
</tr>
<tr class="aw_4 ">
<td class="aw_1 ">data10</td>
<td class="aw_2 ">data11</td>
<td class="aw_3 ">data12</td>
<td class="aw_4 ">d12</td>
</tr>
</table>
Okay, now returning to the recursive template inclusion (Dr Parr's rule 3).
I've added another prefix at_. Our four prefixes are aw_, ac_, aa_ and at_.
That's all I've found are needed so far. Template inclusion is straightforward
<div class="at_otherfile">this will be replaced by the other
template</div>
will replace the inside text with whatever the
otherfile.html template will produce. In this case, the otherfile.html
will have the same datasource and be in the same namespace as the div
it's included within. For looping over a list of items, the programmer
could make them all namespaces and the template writer would set the
class to "at_otherfile aw_list-1", where list is the multivalued
attribute which is itself a namespace.
That rounds out the Ante philosophy. I've been mostly describing things from the template writer's point of view, since making things understandable to the template writer was one of my primary concerns. That reminds me of the other concerns I had:
Here's a simple example of how to use the Ante web template engine. The programmer might write something like this.
<?php include_once 'path/to/Ante.inc'; // some data which could as easily come from a database $datasource = new AwArrayDataSource( array( 'name'=>'John Smith', 'address' => '17 Avondale Crescent', 'city' => 'Kingston', 'province' => 'Ontario')); Ante::render('m.html', $datasource); ?>
And the web designer would work with a piece of html like so:
<html><head> <title>This is some sample text</title> <style> .hello { color: red; } address { display: inline;} </style> </head> <body> Hello <span class="hello aw_name">Mark</span>. <pre> I hear you live at <address class="aw_address">123 Straight Street.</address> <address class="aw_city">Calgary</address>, <address class="aw_province">Alberta</address> </pre> </body></html>
And the name, address, city and province would all be substituted correctly.
A more complicated example would be like this:
<?php include_once 'path/to/Ante.inc'; // some data which could as easily come from a database $datasource = new AwArrayDataSource( array( 'name'=>'John Smith', 'address' => '17 Avondale Crescent', 'city' => 'Kingston', 'province' => 'Ontario', 'children' => array(3, 'Bob', 'Margret', 'Jill'))); Ante::render('m2.html', $datasource); ?>
and the designer would work with something like so:
<html><head> <title>This is some sample text</title> <style> .hello { color: red; } address { display: inline;} </style> </head> <body> Hello <span class="hello aw_name">Mark</span>. <pre> I hear you live at <address class="aw_address">123 Straight Street.</address> <address class="aw_city">Calgary</address>, <address class="aw_province">Alberta</address> and your <span class="aw_children">1</span> children are <ul class="aw_children"> <li class="aw_1">Mark's Son</li> </ul> </pre> </body></html>and the name and address will be replaced as before. Also, the number of children will be replaced in the appropriate span and the whole list of children; one li element for each child: Bob, Margret, and Jill.