Kidomi - a JSON-based templating library

Posted on 05 January 2014 in Articles • 3 min read

Certainly, ClojureScript plays a major role in why the templating syntax seems so natural and pleasant, e.g.:

1
2
3
4
5
6
(node
[:span
  {:style
    {:color "#aaa"
     :text-decoration "line-through"}}
  "hello world!"])

But with JavaScript arrays and objects, there is a way to create something similar

1
2
3
4
5
kidomi(['span',
       {'style':
         {'color': '#aaa',
          'text-decoration': 'line-through'}},
       "hello world!"])

Which outputs a HTMLElement with the following nested structure:

1
2
3
<span style="color:#aaa; text-decoration:line-through">
  hello world!
</span>

Kidomi is written in CoffeeScript. It is covered by unit tests via QUnit and can be used by a Google Closure compiler in an ADVANCED_MODE compilation or separately (e.g. to produce a minified output).

Usage

The kidomi(data) function returns a HTMLNode constructed from a data, for example:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
elem = kidomi(
     ['div#main.content',
         ['span', {'style': {'color': 'blue'}}, 'Select file'],
         ['form', {
             'name': 'inputName',
             'action': 'getform.php',
             'method': 'get'},
         'Username: ',
         ['input', {'type': 'text', 'name': 'user'}],
         ['input', {'type': 'submit', 'value': 'Submit'}]]])

The generated HTML element is:

1
2
3
4
5
6
7
8
<div id="main" class="content">
  <span style="color: blue;">Select file</span>
  <form name="inputName" action="getform.php" method="get">
    Username:
    <input type="text" name="user"></input>
    <input type="submit" value="Submit"></input>
  </form>
</div>

Syntax

The general syntax of kidomi is: node = kidomi(parsableObject). Here, node is a HTMLElement or in a more generic case a DOM node.

The parsableObject is:

  • A string. The returned object is a Text node.
  • A number. It is automatically converted to string and the returned object is a Text node.
  • A node. The returned object is the same node.
  • An array. This should be discussed a bit thoroughly:

The syntax of the parsableObject array is simple and very flexible. It consists of at least one item, which is:

1
['element#id.class1.class2.classN']

Here, id - is the id attribute of the node, class1.class2.classN - CSS classes of the node, i.e. class="class1 class2 classN".

For example:

1
2
3
4
5
6
['div']                 // <div></div>
['div#content']         // <div id="content"></div>
['span#user.username']  // <span id="user" class="username"></span>
['span.password']       // <span class="passwordd"></span>
['div.main.dialog']     // <div class="main dialog"></div>
// etc.

The second item is either an attributes object, or a sub-parsableObject. The attributes object has the following syntax:

1
2
3
4
{'class': ['class1', 'classN'],
 'style': {'prop1': 'val1', 'propN': 'valN'},
 'attribute1': 'value1',
 'attributeN': 'valueN'}

or

1
2
3
4
{'class': 'class1 classN',
 'style': 'prop1:val1; propN:valN;',
 'attribute1': 'value1',
 'attributeN': 'valueN'}

The class and style key-value pairs or strings are optional.

  • The class key-value pair is an array or a string with CSS classes' names applied to the node. It is appended to the classes found in the first item of the parsableObject array.
  • The style key-value pair is an object or a string of CSS style properties of the node.

The attributeX key-value pairs are the attributes of the node.

For example:

1
2
3
4
5
['a', {'class': ['biglink'],
       'style': {'color': 'red'},
       'href': 'http://github.com'}]

// <a href="http://github.com" class="biglink" style="color:red;"></a>

The rest of the array items are nested parsableObjects or in a special case - an array of arrays with parsableObjects. For example:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
// Nested elements
['div', ['div', ['span.name', 'Name: '], ['span.lastname', 'Last name: ']]]
/*
   <div>
     <div>
       <span class="name">Name: </span>
       <span class="lastname">Last name: </span>
     </div>
   </div>
*/

// Expandable nested array
['tr', [['td', 'First'], ['td', 'Second'], ['td', 'Third']]]
/*
 <tr>
   <td>First</td>
   <td>Second</td>
   <td>Third</td>
 </tr>
*/

Building and testing

You will need the following tools to build and test kidomi:

  1. GNU Make. This is used to run the Makefile script.
  2. CoffeeScript compiler. This is enough to build the library.
  3. Google Closure compiler. This is used to build the optimized version of the library. The CoffeeScript code is written with the Closure restrictions in mind.
  4. PhantomJS is used to run the unit tests from a shell. You can as well run them in a normal browser.

Advanced usage

Referencing elements

One of the patterns where kidomi might be especially handy is when you have to create certain HTML elements before adding them in a DOM structure. For example:

1
2
3
4
5
6
7
8
9
button = kidomi(['button']);
button.onclick = function(){ alert('Hello world'); };

myDiv = kidomi(
            ['div',
              ['span', 'Click me:'],
                button]);

document.body.appendChild(myDiv);

List comprehensions in CoffeeScript

List (array) comprehensions are very handy to use as the expandable array elements, for example:

1
2
3
4
5
['tr', [['td', '1'], ['td', '2'], ['td', '3']]]

# can be written as:

['tr', (['td', "#{i}"] for i in [1..3])]