I have been working on a templating engine that uses only valid HTML to define the templates. It uses simple conventions in order to produce markup from JSON and a template. The world doesn't need another templating language. But I want a different solution than the ones that exist. This post is as much about why as it is about what.
TL; DR
I made a thing. You can see the code on GitHub and see the humble beginnings of the docs for it here. If you do read the whole thing, just imagine it started as an elevator pitch but the elevator broke and you're going to be stuck with me for quite some time…
Disclaimer
Developers have worked hard to create templating approaches that are performant, viable solutions. My criticism of certain aspects of those solutions isn't criticism of individuals or their ability. I'm just super particular. Let's take a moment to laugh at me:

There's An Amazing DSL For Describing UI
It's called HTML. It's not the new hawtness. The DOM has and will continue to hurt feelings and crush dreams. On the upside, there is something nice about clean HTML that is unmarred by odd-ball, proprietary magic strings that mean something to a pre-processor. Don't get me wrong, I love arbitrary spin-off syntax just as much as everyone else, but sometimes it's nice to stick with open standards that are well known across the industry.
You Got Your Egg Nog In My Goat Milk!
I guess my primary complaint with the predominant approach is the rampant abandon with which imperative and declarative styles; markup and source are blended into a hard to cypher, tightly coupled mass of untestable concerns.
I dream of a world where templates look like this:
<ul data-id="people">
<li><span data-id="firstName"></span> <span data-id="lastName"></span></li>
</ul>
and not like:
<ul>
{{#each people}} // srsly? I have to tell it to iterate?
<li>{{firstName}} {{lastName}}</li>
{{/each}} //lulz, it doesn't know when it's done…
</ul>
or like:
<ul>
<% for ( var index = 0; index < people.length; index++){ %>
<li><%= people[index].firstName %> <%= people[index].lastName %></li>
<% } %>
<ul>
// are your eyes screaming too?
All About Trade Offs
Whining and snark aside, each approach is making the choice to accept some drawback in order to obtain specific advantages and features. Most of the traditional template engines compile the template into a function that can be parameterized and invoked. This approach sacrifices testability and separation of concerns to get speed and the ability to embed logic and adaptability into the template.
I am willing to sacrifice a little speed and adaptability in the markup in order to reinforce testability, adhere to standards and keep my solution clean and maintainable.
Why Meet What
I started writing Cartographer about 5 months ago and it's gone through a lot of major changes in that period of time. It still has a way to go, but in the end, Cartographer's main goal is to provide a convention based, HTML 5 templating engine that's simple to use yet powerful and flexible. It's never going to run as fast as say, Transparency, but a refactor to improve the performance by a little over 100% and reduce the number of dependencies is in the works (and Transparency uses classes for binding and will never hold a candle to Cartographer's feature set).
Basics
Cartographer does not render markup onto the page. It invokes your callback with the markup and a small bit of metadata about the markup so you don't have to try and track the context of the original call yourself. Cartographer is asynchronous only. It will never have a blocking implementation. This is skipping ahead a bit, but the reason is primarily because nested templates could get specified and loaded in at any point during the generation of markup.
Once a template has been created, you can choose to generate additions to the original markup or render specific portions of the original markup. This means you can define a parent template that may be a composite of several templates but generate any portion of that template with new model fragments and then choose how and when to put that new fragment on the screen.
// create a template
Cartographer.map(templateName);
// generate markup from template
Cartographer.render(templateName, id, markup, function( id, markup, operation ) {
// id is the template's id
// markup is the generated content
// operation is either "render", "update" or "add"
});
Cartographer has a few advanced features that allow it to do things that other JS template engines don't (or can't). So here's a few to give you an idea.
Where Do Templates Come From?
Templates can come from any source. Out of the box, Cartographer looks in the DOM for the template and then asks infuser for the template. You can provide your own methods for getting templates and control when they get checked by either appending or prepending them to Cartographer's list of template sources.
// This appends this source to the end of sources Cartographer will check first
Cartographer.resources.appendSource( function( templateName, onSuccess, onFail ) {
// check for the template
// if you have it, call onSuccess with the text content
// otherwise call onFail()
});
// This prepends this source to the end of sources Cartographer will check first
Cartographer.resources.prependSource( function( templateName, onSuccess, onFail ) {
// check for the template
// if you have it, call onSuccess with the text content
// otherwise call onFail()
});
Combining Templates From Markup
A template can specify that another template should be used in place of an element by setting the data-template attribute:
<div data-id="person">
<!-- this span element will not be included in the final markup -->
<span data-template="person-details"></span>
</div>
Combining Templates From Model
A model can choose to have a different template render any portion of it (even including values) by adding a __template__ member.
{
"firstName": "Alex",
"lastName": "Robson",
__template__: "simple"
}
When you need a template for a value, it's a bit more complicated because you need to communicate both the value of the property and the template. This requires changing the value to a hash that includes value and template:
{
"height": "6'10\"",
"weight": { __value__: 254, __template__: "pounds" }
}
Is it more work? Absolutely. In this case, probably too much work. It'd be nicer if there were a way to use a function for that kind of case…
Fun With Functions
Which is why you can. Instead of letting you embed code in your template, Cartographer only allows code in your code. The Xzibit "Yo Dawg" meme writes itself:
{
"firstName": "Alex",
"lastName": "Robson",
"fullName": function() { return this.firstName + " " + this.lastName; }
}
Sure, it's not JS in your HTML (or vise versa) but it's not like I have to explain how this works. Best of all: you. can. test. this. approach.
But wait, there's MOAR!
Anything in the model that I've shown you can also have a function. So just imagine…
{
"height": "6'10\"",
"weight": {
__value__: 254,
__template__: function() {
if( this.weight > 250 ) {
return "pounds-warning";
}
else {
return "pounds-healthy";
}
}
}
}
Ok, so before you nerd-rage, I am not suggesting the whole function body belongs IN the model, you can just put the function handle there and define it elsewhere.
Status
Cartographer is alpha-ware. It has tests and what-not. I'm interested in feedback from people who are actually interested in using it and would be happy to know if it's helpful. If not, I've learned a lot working on it and will use it in future pet projects of mine.
Too Many Words
If you haven't decided to climb through the ceiling tiles and try your luck climbing out of the elevator shaft, there are places to go from here. Checkout the code, look at the docs (there's a complete running example on the main page) and ping me via Twitter or my GitHub. The project even has issues so you can make requests or notify me of all the broken stuff.
