If you’re writing a webapp using rdfa-lite, It makes sense to consider using rdfa markers in the DOM based template engine.
While working on round-trip html5 <-> Rdfa-lite query and editing, I’ve been struck by the primitive feeling of html template languages. {{mustache}} , <%escapes%> <–%ssi escapes %–> all work the same way as C preprocessor macros – string concatenation style – which ignores the underlying structure that we’re working with.
Thankfully, HTML5 is changing everything – it has / will have a template tag
Along the way to finding that, I came across Weld.js (dead?), Transparency or Plates and Pure and not forgetting Knockout.js.
None of them quite goes where I want – as they either use class/id, or their own data- attributes.
What I’m after, is leveraging the already existing semantic annotations of existing (or template tag) elements to add new ones.
For example, I might have the following Link menu, and then want to dynamically add others from a remote query
<ul id="page-list">
<template id="page-item-template" style="display:none;">
<li typeof="WebPage" resource="/">
<a property="url" href="/" tabindex="-1" id="index">
<span property="name">Home</span></a>
</li>
</template>
<li typeof="WebPage" resource="/">
<a property="url" href="/" tabindex="-1" id="index">
<span property="name">Home</span></a>
</li>
<li typeof="WebPage" resource="/SvenDowideit.html">
<a property="url" href="/SvenDowideit.html" tabindex="-1" id="SvenDowideit">
<span property="name">Sven Dowideit</span></a>
</li>
</ul>
The following works for browsers that don’t support the new HTML5 template tag:
var node = document.querySelector('#page-list [typeof=WebPage]').cloneNode(true);
node.querySelector('[property=name]').textContent = 'TODO';
node.querySelector('[property=url]').href = '/TODO.html';
document.querySelector('#page-list').appendChild(node);
A nice start, but to me, there’s still something not right with the addressing scheme –
which is where some of the above template engines use the @ symbol to denote that the value should be set on the named attribute.
something like this might work
var node = document.querySelector('#page-list [typeof=WebPage]').cloneNode(true);
node.render( {
'[property=name]@textContent': 'TODO',
'[property=url]@href': '/TODO.html'
});
document.querySelector('#page-list').appendChild(node);
but setting up the relationships beforehand, and making the clone implicit
var template = getTemplate('#page-list [typeof=WebPage]', {
name: '[property=name]@textContent',
url: '[property=url]@href'
});
document.appendChild(template([
{name: 'Sven Dowideit', url: '/SvenDowideit.html'},
{name: 'TODO', url: '/TODO.html'}
]);
Which looks an awful lot like Weld.js’ (and the inverse of Pure?) API. And, should be trivial to implement using Transparancy.
in the process of thinking it through, I wrote a simplistic version that deserves replacing when I’m thinking about something else:
getTemplate: function(templateSelector, map) {
if (this.Template === undefined) {
var template = document.querySelector(templateSelector);
var template_map = {};
for (var key in map) {
var address = map[key].match(/^([^@]*)@?(.*)?$/);
template_map[key] = {
node: address[1] || '*',
attr: address[2] || 'textContent'
}
}
this.Template = function(values) {
var new_elements = [];
for (var idx in values) {
var elem = template.cloneNode(true)
for (var key in values[idx]) {
if (template_map[key] !== undefined) {
var node = elem.querySelector(template_map[key].node);
//TODO: detect if key is a method, and call it?
if (template_map[key].attr === 'textContent') {
node.textContent = values[idx][key];
} else {
node.setAttribute(template_map[key].attr, values[idx][key]);
}
}
}
//TODO: if it where a real template tag, apparently there would be an elem.content
new_elements.push(elem.children[0]);
}
return new_elements;
};
}
return this.Template;
}}