YATE – JavaScript Templating Engine for Agile UI Development

Recently we were looking for ways to do rapid building of prototype pages during our UI development. I still remember my first web project when I built an air ticket reservation system over a decade ago. Back then, we were concatenating HTML / Javascript inside servlet code to crank out the UI. The joy of building a cool new system was quickly offset by the frustration in dealing with escape / unescape single double quotes for multiple layers of Javascript inside Java code. Over these years, things evolved a lot in trying to address this separation of logic and presentation issue. We had JSP, Strut, Velocity, FreeMarker etc. They were running toward that goal with one getting closer than the other. But logic code still mingles with HTML/CSS . If not used cautiously, they can still yield spaghetti code faster than developers can chew.

In my opinion, the simplest and fastest way to realize a design mockup is to build it directly in DHTML/CSS with effects/animations controlled by Javascript. The rapid building of pages can help our design team and management do iterative evaluation easier. After the UI is finalized, we want to be able to use all this work and hook up real data with AJAX calls to make the page live instantly. The goal is to find a practical approach that can completely separate the UI code and logic code development, then bridge them easily with real data at a very late stage.

During the search, we evaluated two Javascript packages: Sammy.template and chain.js .

Sammy.template

Sammy template is a nice Javascript framework built on top of JQuery. Here is what we like and don’t like about it:

– pros:

1. very fast
2. precompiled template, reusable in later calls

– cons:

1. can NOT use inline HTML as the template, which means you’re not able to use what you see directly as the HTML template
2. HTML and JS code mingled and tied up together
3. no flexibility in using different logic code on different data

chain.js

chain.js is a neat data binding tool also built on top of JQuery. Here is what we think about it:

– pros:

1. can use inline HTML as template
2. separation of HTML and Javascript code
3. easy to apply different logic on different data
4. chainable with other JQuery use.

– cons:

1. very slow (because everything is direct DOM manipulation)

YATE

Since both packages did not fit our bill, we decided to build our own. Here comes YATE (yet another template engine), which fulfills all our requirements:

–Clean boundaries between view, data and logic; template, data, logic are independent of each other
– Built as a JQuery plug-in, works with JQuery calls
– Total separation between HTML and Javascript code, no JS code in HTML template at all
– Flexibility in providing customized logic code on different data
– Templates are precompiled and cached, very fast in page rendering
– easy to use and maintain in development.

OK, now let’s roll and see some actions!

To use YATE, there are 3 parts to make it work.

1. template
2. data and logic (optional)
3. how to invoke

You may play along with the example here. And the entire code base can also be found in here.

In this example, we show a static table on top and post-render table with real data by YATE in the bottom.

1.template

In the textarea on top is the HTML template. As you can see, YATE uses <y:t variable_name ></y:t> to bind data to the node. Everything inside <y:t … > … </y:t> will be replaced by the corresponding value in data or a function will be invoked if there is a matching one in the pass-in logic. Tag name y:t is short for yate:template, because modern browsers will just faithfully ignore the tags that they don’t understand, the template uses it as a way to pass the message to YATE that the content of the tag is what needs to be changed, such as:

<td ><y:t status.queuenum>-</y:t></td>

status.queuenum will map to the numbers in the data.

As to the replacement of attributes, YATE uses {y:t variable_name}{/y:t} as the matching pattern because browsers don’t allow < inside a tag.

  < a href="{y:t link}  #  {/y:t}">
    <y:t title>
       SIP Inject-Overflow Mutations
    </y:t>
  

The value of ‘link’ in the data will be used as the href for the anchor.

And the tag <y:t remove > … </y:t>, YATE will remove everything in that tag from the template before the template is used to bind data.

  <y:t remove>
        <div style="height: 8px; margin:3px 0px;">
          a green bar
	</div>
  </y:t>

2. data & logic

In the textarea in the bottom, the upper part is data and the lower part is the logic.

The data is a JSON format array with its elements corresponding to the rows in the table. Each y:t tag must have an exact match in the data, otherwise YATE will throw a Javascript error to remind you of the mismatch. You can nest objects or arrays in the JSON data.

You may optionally pass in logic to YATE. The logic must be a Javascript object where each of the variables in it is a defined function. When a <y:t …> tag matches the variable name, such as “status.progress”, the corresponding function will be invoked and passed in these arguments:

{
  &ldquo;data�: &hellip;&hellip; , // the entire data object you pass to YATE
  &ldquo;index�: &hellip;&hellip;, // current processing index of data
  &ldquo;node�: &hellip;&hellip;  // the innerHTML of the <y:t &hellip;> </y:t> tag
}

so you will be able to run it through your own logic. The returned HTML will be inserted into where the <y:t …> tag is.

If you don’t pass in logic to YATE, YATE will replace the <y:t …> tag with whatever the value in the data object is.

3. how to invoke YATE

Here is the general format:

$.yate( {
          &lsquo;template_selector&rsquo;: �template selector�,
          'data':  data,
'options': logic,
          &lsquo;returnHtml&rsquo;: true,
	&lsquo;debug&rsquo;: false
});

Basically there are two ways to invoke and use YATE:

first:

$.yate({template_selector: &lsquo;#tamplateonly  table tbody', 'data': data, 'options': logic });

YATE takes in a JSON object as its argument. In it, ‘template_selector’ and ‘data’ are mandatory. ‘options’, ‘debug’ and ‘returnHtml’ are optional.

‘template_selector’ is the selector path to get to the template HTML. In our example, ‘#tamplateonly table tbody’ will lead to use

  <tr class="content-row" >
 	<td style="text-align:left;">
 	    &hellip;&hellip;
       </td>
  </tr>

as the template to bind data. The value of “template_selectorâ€? is also used by YATE as a key to store the precompiled template function in its internal cache, so it can be reused in later calls immediately without compiling again.

‘data’ is the JSON data object. It can be an array, such as for multiple rows in our example; or just an object in the case of a single row.

‘options’ is optional, with each variable associated to a defined function.

‘debug’ is optional as well. YATE will output the template function to the firebug console when it’s set to true.

‘returnHtml’ is optional, default as false. In this case, YATE will bind data to the template and replace the original data (where the ‘template_selector’ points to) with the results. When ‘returnHtml’ is set as true, we have our second form of use:

second:

var resultHtml = $.yate({template_selector: &lsquo;#tamplateonly  table tbody', 'data': data, 'options': logic ,&rsquo;returnHtml&rsquo;:true});
$(&lsquo;#someOtherPlace&rsquo;).html(returnHtml);

When ‘returnHtml’ is set to true, YATE will only bind data to the template and then return it as HTML. It will keep the original template intact as where it’s. The use case is for applying the template result to multiple places.

Performance

Here is the chart of the time spent to render the bottom table in our example, these were run on Mac with Firefox 3.5

YATE performace vs sammy.template and chain.js

YATE uses 10ms , about the same as sammy.template.

Size

YATE:
original(without comments) — 2K
after minified and Gzip — 669 bytes

Sammy.template:
original — 5K
after minified and Gzip — 511 bytes

Current limitation

Here are a few:

1. tags can not be nesting with each other. The following doesn’t work:

<y:t A>
     <y:t B>
          .........
     </y:t>
</y:t>

If you need to render a complicated layout, we suggest to use a function to handle it.

2. {y:t …}{/y:t} may not work for some attributes, such as width in style. Because the browser always expect numeric for width, height etc, so anything not numeric will be removed. For such cases, please use pass in function to handle. And for most of the attributes, such as href, title, class etc, {y:t …}{/y:t} work.

3. <y:t remove> … </y:t> tag is for telling YATE to remove anything in between. It can be placed as the direct child for most tags, such as

<td >
  ......
  <y:t remove>
    <div style="height: 8px; margin:3px 0px;">
      ......
    </div>
  </y:t>
</td>

but not for these tags table, thead, tbody, tr . Because for those tags, only a limit set of tags are allowed by the browser to be their direct children. Like tr, only td is allowed by the brower, all others will be removed. So in order to remove table, thead, tbody, tr from the template, please use this style:

<tr y:t_remove>
  <td>......	 </td>
  <td>......	 </td>
</tr>

We think YATE is a small yet powerful tool that can help ease the pain of template/data binding in UI development. It’s fulfilling its job so far. If you have any questions, comments, suggestions or find any bugs, please write to us. We would like to see it healthily grow and expand its ability.

YATE!

Bookmark and Share