Bespin Developer's Guide
Template Engine
Using The Bespin Template Engine
The template engine is designed to be all the things that everyone always claims like easy to use etc, but specifically: Lightweight (if we need more than 300 lines of code, then someone else can probably do it better) DOM based - that is to say we process the DOM, not strings. This allows us to register event handlers and provide better access to constructed elements.
Creating a Template
Create a file called templates/whatever.htmlt
in your plugin. The
important parts are that the file is in a templates directory at in the root of
you plugin, and called *.htmlt
.
The file is an HTML fragment. It is not restricted to a single root, however since the fragment is NOT automatically added to the document, this will be the common way to do things.
An example template could look like this:
$ cat bespin/plugins/foo/templates/bar.htmlt <div save="${parent.element}" id="hello">${contents}</div>
This would then be used as follows:
var templates = require('foo:templates'); templates.bar({ parent: this, contents: "world" }); console.log(this.element.id); // hello console.log(this.element.innerHTML); // world
${...} in General Attributes
Any ${} element will be processed as a portion of Javascript, in the context of the object passed to the templates.name function (i.e. templates.bar in the example above)
The 'save' attribute is a special attribute to record the current element. See below for more details.
${...} can show up in elements and in text nodes, and will be processed accordingly. A ${...} block contains arbitrary Javascript, but should return something. Generally however it is recommended to stick to a dot path from an attribute passed to the template.
save="${...}" Attributes
The save attribute is special. It takes the current node at sets it into the pointed to structure. In this case ${} is not arbitrary Javascript but a dot path to an element to set.
on***="${...}" Attributes
Events are registered using onevent handlers that look similar to the way events are normally set in HTML, however there are some things to be aware of.
Example:
<div onclick="${parent.mouse}>Hello</div> var templates = require('foo:templates'); this.name = 'Fred'; this.mouse = function(ev) { console.log(this.name, ev); }; templates.bar({ parent: this });
Here we are registering an onClick handler for the new div, which, when clicked
simply echos this
and the event object to the console.
This form is particularly handy when the handler to be registered is part of the
parent
object without needing to be especially created as above, and for this
reason we automatically bind this
to the object that contains the function
(i.e. what you might like Javascript to do automatically)
For event listener registration there are 2 things to look out for:
Although it looks like we are using DOM level 0 event registration (i.e.
element.onfoo = somefunc) we are actually using DOM level 2, by stripping
off the 'on' prefix and then using addEventListener('foo', ...). Watch out
for case sensitivity, and if you successfully use an event like DOMFocusIn
then consider updating these docs or the code.
Sometimes we might need to use the capture phase of an event (for example
when processing mouse or focus events). The way to do that is as follows:
onfocus="${object.handler [useCapture:true]}"
. Currently the only
supported option is useCapture, and it must be specified EXACTLY as the
example. In the future we might add other options, or make the syntax
simpler.
It is very likely that the useCapture syntax will change in the future, probably
to something that does not invade the ${...} space possibly something more like
onfocus="${object.handler}" onfocususecapture="true"
Future
2 future directions are being considered for this template: if="${some_boolean}" and loopFor="${some_array}" attributes. The former allows the template to prune some part of the output unless a condition is met. The latter allows a node to be repeated a number of times with different values for each member of the array. 2-way templating. For form fill applications, it can be useful to allow changes to input elements to be automatically reflected in the original structure, or even for changes in the original structure to be reflected in the HTML. This last example is probably too complex for our purposes however.