A simple, fast, & safe, mustache/handlebars like templating engine with data-binding hooks built in.
JavaScript Makefile
Clone or download
Fetching latest commit…
Cannot retrieve the latest commit at this time.
Permalink
Failed to load latest commit information.
bin Fix: cli compiling error, fixes: #3 Jun 30, 2014
demo Add call and apply Dec 6, 2014
lib Add call and apply Dec 6, 2014
test Fix unquoted/escaping for curlies. Nov 23, 2014
.gitignore Merge pete's refactor Jul 29, 2014
Makefile Update demo Jul 30, 2014
README.md Update README.md Jul 11, 2015
TODO.md Update todo Nov 23, 2014
domthing.js Fix whitespace bug. Jun 11, 2014
package.json 0.4.0 Dec 6, 2014
runtime.js Lots of refactoring: Jul 30, 2014
testem.json Add testem Dec 6, 2014

README.md

Deprecation notice

Due to the growth in popularity of react, and my own enjoyment using it, domthing is effectively being unused and unmaintained by me at this point.

If you are interested in picking it up and developing it let me know, otherwise, sorry.


domthing

What is this?

A DOM-aware, mustache/handlebars-like, templating engine. Heavily inspired by HTMLBars.

How do i I use it?

Check out the demo repo at http://github.com/latentflip/domthing-demo

npm install -g domthing

Now create a directory of templates, ending in .dom, like 'foo.dom':

<!-- foo.dom -->
<h1>{{ greeting }}</h1>
domthing path/to/templates > path/to/templates.js

now in your app you can do:

var templates = require('./path/to/templates.js');

document.addEventListener('DOMContentLoaded', function () {

    //render the template (returns a dom node);
    var rendered = templates.foo({ greeting: 'Hello!' });

    //append the node
    document.body.appendChild(rendered);

    //trigger updates to context options:
    setInterval(function () {
        //or whatever
        rendered.update('greeting', 'Goodbye!');
    }, 5000);
});

Why?

Most templating engines are just string interpolation, precompiling this:

<a href="<%= me.url %>">My profile</a>

generates a function like this:

function template(context) {
    return '<a href="' + context.me.url + '">My profile</a>';
}

which you call like this:

someElement.innerHTML = template({ me: { url: 'twitter.com/philip_roberts' } });

This works, but it's not very smart. If you want to update your page if the context data changes you have to rerender the template (slow), or you have to insert css selector hooks everywhere so that you can update specific elements, a la: <a role="profile-link" href="<%= me.url %>">My Profile</a> and then $('[role=profile-link]').text(me.url).

You've also now split the knowledge of where data goes into the dom in the template into two places, once in the template, and once somewhere in JavaScript land. Or you just do it in JavaScript land and your templates look a little empty. You also better hope nobody changes your html in a way that breaks your css selector code, or you'll be sad :(. Also you've now made it harder for frontend devs who might be comfortable editing templates & styling, but less so tweaking css selector bindings, to actually edit where data goes in your templates.

So, what if your template engine actually understood how the dom worked, and actually returned DOM elements:

//warning, code for illustrative purposes only:
function template(context) {
    var element = document.createElement('a');
    element.setAttribute('href', context.me.url);
    element.appendChild(document.createTextNode('My Profile'));
    return element;
}

And now that you had actual references to real elements, you could just bind them to data changes directly, no css selectors required:

//warning, code for illustrative purposes only:
function template(context) {
    var element = document.createElement('a');
    bindAttribute(element, 'href', 'context.me.url'); //updates href when context changes
    element.appendChild(document.createTextNode('My Profile'));
    return element;
}

If you had that you could trivially write templates that did this:

<a class="call-to-action {{model.active}} {{model.linkType}}">Buy Stuff!</a>

and the classes would all just work, and update with the data, or this:

<a class="call-to-action">
    {{#if user.hasBought }}
        BUY MORE!
    {{#else }}
        BUY SOMETHING!
    {{/if }}
</a>

and the output would update as soon as user.hasBought changed.