README
Build Status">
x-template
============
This module offers simple DOM based templating by merging in JSON and/or DOM data sets using css selectors.
Example: DOM Template + JSON Data
Assume you have the following JSON data:
var o = {
users: [
{ person : { name: 'Joe' , age: 20 , active:true, address: { street: 's1' } } },
{ person : { name: 'Alice', age: 35 , active:false } },
{ person : { name: 'Bob' , age: 40 , active:true } }
]
};
combined with the following template:
<ul data-is=".users" >
<li data-is=".person" >
Name:<span data-is=".name">-</span><span data-if=".age"> (<span data-is=".age"><span>)</span>
</li>
</ul>
<span data-not=".users">No users available</span>
the result is:
<ul>
<li>
Name:<span>Joe</span><span> (<span>20</span>)</span>
</li>
<li>
Name:<span>Alice</span><span> (<span>35</span>)</span>
</li>
<li>
Name:<span>Bob</span><span> (<span>40</span>)</span>
</li>
</ul>
Example: DOM Template + DOM Data
Assume you have the following part of an RSS feed:
using the following template:
<section data-is="channel"
<h1 data-is=">title">TITLE</h1>
<a data-to-href=">link" data-$txt=">description" ><pan data-is="$txt">description</span></a>
<ul data-if="item">
<li data-is="item"><a data-to-href="link" data-$txt="title" ><pan data-is="$txt">news</span></a></li>
<ul>
</section>
the result is:
The basic templating function is thus as follows: DOM x DATA -> DOM, where DATA = JSON or DOM (HTML/XML)
Templating means here recursively walking top down and for each template DOM element
- select a subset from the provided data
- linking a new DOM element for each result in the selected data
- merge the result into the result DOM element (data2dom)
Selecting Data
We all know how to use css selectors to query a tree of dom elements to get a result set of DOM nodes. Here we use a syntax like the css selector syntax to perfrom a query on a tree of JSON data and get a as a result a set of JSON objects
The syntax is described in x-select
Linking DOM Element to Data with data- Attributes
A 'data-' attribute on a DOM element is used to define relation between a result DOM element and the selected data
data-if clone this element if there is data
data-not clone this element if there is no data
data-is clone this element for each data object in the result set, then merge the data object into the element and use that value as data for all sub template elements
data-to-'attribute' attribute is an attribute name where the result is rendered into. p.e. data-to-href
Scope
A DOM element is related to a result from a set found by a 'data-is' selector. All selectors of sub elements are evaluated on this result data. Therefore a DOM element with a data-is forms a implicit data scope for all subelements.
You can escape from that scope using :root or a variable (see below)
Data2DOM
The real rendering is defined by an array of functions mapping a data object (JSON or DOM) into a DOM element
There are some predefined renderers, you can add your own ones by passing them in the options.data2dom as array.
each renderer has the following interface:
function(element, value, options, next){
// element is the dom element where the value should be rendered into
//
// value is what can be rendered into the element.
// This can be string, but also an object, or a DOM element
//
// options option passed through from the main template call, you can use it p.e. to pass a logger
//
// this for JSON rendering this is the js-traverse context of the value.
// this.key for example returns the name of the key at which value was found in an object
// **Here you can inspect the element and the value and maniupulate the dom tree**
// be sure to call then next:
// next Use next(null,value) and pass the (changed) value to the next handler
// or call next(true,value). True indicates you handled the value and no further
// data2dom handler is called.
//
// The passed value is used by the next data2dom handler and data-is templating scope for sublements
}
The following data2dom handlers are predefined:
date if the key of the value contains Date or date the value is formated using the given html lang and put into the elements innerHTML url if the key of the value is url and the el has a href, action, src attribute the url is rendered in that one. input renders values for input fields: textarea: set value in innerHTML input, select, option, button set the attribute value (not for type checkbox) img set the src of the image text default replace the elements textContext with a value convertet to string, if it is not an object dom2dom This renders the value if the value is an dom element. As this can be quite complex you can pass a debug:true in the options, to get some extra comments in the result to see what happened.
attribute handling
-------------------
Attribute values are copied: a simple set, the 'class' attribute is different: value are concatenated, seperated by blanks.
The following attributes in the template element can define what should be copied/merged. Values are whitespace separated:
'data-attributes' which attributes can be copied, * means all
'data-attributes-exclude' which attributes should *not* be copied, * means all
'data-class' which classes should be added, * means all
'data-class-exclude' which classes should *not* be added, * means all
Inner HTML
'data-option' if this contains 'inner' then the complete innerHTML is copied 1:1
text node handling
------------------
'data-exclude-texts' Use true to indicate *not* to copy text nodes
All text nodes from the target are replaced each by those of the source. Note that text nodes can
occur multiple time inbetween the subelementes of the template element. Hence this rule.
If the target has more text nodes then the source these are removed.
If the source has more they are appended at to the last one.
Apply / Recursion data-apply
'data-apply' Is a template attribute containing a DOM css selecter of the template (not the data source!) which defines the next elements to be used as child elements of the current template element during templating.
This allows to use arbritray snippets of a page as templates and break up a single template in smaller parts. More important it allows to template recursive structures of arbitray depth. Example: A backend sends a tree with nodes like a->b->(c,d->(x,y,z))
data:
{ node: 'a', children:[
{ node:'a.1', children: [
{ node:'a.1.I' },
{ node:'a.1.II', children: [
{ node: 'a.1.II.X'},
{ node: 'a.1.II.Y'},
{ node: 'a.1.II.Z'}
]}
]},
{ node: 'a.2', children: [
{ node: 'a.2.I'},
{ node: 'a.2.II'}
]}
]* }
template:
<div data-template="node-content">
<span data-is="> .node"></span>
<ul data-if="> .children" >
<li data-is="> .children" data-apply=":root [data-template='node-content']" ></li>
</ul>
</div>
result:
<div data-template="node-content">
<span>a</span>
<ul>
<li><div data-template="node-content">
<span>a.1</span>
<ul>
<li><div data-template="node-content"><span>a.1.I</span></div></li>
<li><div data-template="node-content"><span>a.1.II</span>
<ul>
<li><div data-template="node-content"><span>a.1.II.X</span></div></li>
<li><div data-template="node-content”><span>a.1.II.Y</span></div></li>
<li><div data-template="node-content"><span>a.1.II.Z</span></div></li>
</ul>
</div></li>
</ul>
</div></li>
<li><div data-template="node-content">
<span>a.2</span>
<ul>
<li><div data-template="node-content"><span>a.2.I</span></div></li>
<li><div data-template="node-content"><span>a.2.II</span></div></li>
</ul>
</div></li>
</ul>
</div>
Note: The attribute name 'data-template' is just a convention, anything can be used by the data-apply selector.