Introduction
Miro is a light-weight template engine for applications which take advantage of Ajax. It focused only on one task: how to make it easy for developers to convert in the browser, a Javascript object or an XML DOM object into a fragment of HTML code that can later be assigned to a div using div.innerHTML. As an Ajax developer, you are forced to make these types of conversion 1) each time you want to dynamically change in the client a part of your document (example: sort a list of rows) or 2) each time you have made an async call to the server using HTTPRequest or <script...> and want to present and display the result to the user. The goal is to get rid of htmlFragment += "some <a href=\"" + var1 +"\"> which is very painful to generate and even more painful to maintain.
In this document, we will show you how to install and use Miro.
About the Author
Edwin Khodabakchian is an entrepreneur looking for the next interesting thing. Prior to founding DevHD, Edwin was Vice President of Product Development at Oracle, driving the design and delivery of the Oracle SOA Suite. Prior to Oracle, Edwin was co-founder and CEO of Collaxa, a start up focusing on BPEL and Web Service Orchestration and which was acquired by Oracle in 2004. Edwin started his carrier at Netscape/AOL where for 5 years, he was exposed to and led the development of various Internet technologies.
Thank you
Thank you to Michal Chmielewski for his feedback and recommendations during the design of Miro.
Thank you to Douglas Crockford and Iain Lamb for the great content they have produced on the YUI channel around Javascript, DOM and advanced UI development.
Usage Overview
Using Miro is a four step process:
- You write the template file (acme.templates.XXXXX.miro)
- You use an ant task to compile the .miro file into a .js file (in this case, acme.templates.XXXXX.js).
- You import the .js file in your HTML using <script src="acme.templates.XXXXX.js" ></script>.
- You invoke a simple function to convert your js and xml objects into HTML.
For a more detailed example, please refer to "Iteration 3: Using a template to generate HTML" in Miro, Ajaxlets and Google Custom Search API.
Installating the Miro Ant Task
The core functionality of Miro is implemented as a Ant task which converts a .miro template into a Javascript function. We decided to implement the functionality as an ant task so that the processing is done once offline and there is no overhead at execution time.
To install the Miro ant task, download the ant-miro.jar and copy it into the lib directory of your ant installation. If you are running Mac OS X, it comes packaged with ant ( see directory /Developer/Java/Ant ).
Creating a Ant Build File for Your Project
Here is an example of ant build file which compiles a .miro template file and into a js file. You can use this as the starting point for adding miro step to you existing ant build.xml file or to create a new ant build.xml.
Code snippet 4 shows the content of the ant build file which converts the .miro file into a .js file.
<?xml version="1.0" encoding="ISO-8859-1"?>
<project name="vulnpedia" basedir="." default="compile">
<taskdef name="miro" classname="devhd.labs.miro.Miro" classpath="${ant.home}/lib/ant-miro.jar"/>
<target name="compile" description="Compile the list of miro templates into js templates">
<miro template="templates/vulnpedia.templates.search.miro" out="scripts" />
</target>
</project>
The miro ant task will generate a Javascript function for each of the <template> element defined in the .miro file. The signature of the Javascript function is the following: [file name].[template name] ( [template parameters] ).
Miro Directives
Here is a list of the miro directives you can use in a .miro file to dynamically generate HTML. We have tried to keep these directives to the minimum set possible.
#for
#for directive is used to iterate through a set of numbers or through all the keys in a map. It is coupled with an #end directive to mark the end of the block being iterated. The syntax after the #for is identical to the Javascript for syntax and supports both the classical for( var i = 0; i < 10 ; i++ ) and the for( var key in some Map ). The only constraint is that it has to be expressed on a single line and it has to be completed with a new line (\n).
Example:
<?xml version="1.0"?>
<templates>
<template name="resultList" parameters="results">
#for( var i = 0 ; i < results.length; i++ )
<p>
<a href="${results[i].url}">${results[i].title}</a><br/>
${results[i].content}<br/>
<span class="url">${results[i].url}</span>
#if( results[i].cacheUrl != null )
- <a class="cached" href="${results[i].cacheUrl}">Cached </a>
#end
</p>
#end
</template>
</templates>
#if( condition )
The #if( condition ) allows for conditional branching. The syntax is here again similar to javascript with the only contraint that the directive has to fit in one line and be completed with a new line. An #end directive shows the end of the conditional block. This directive can be extended with an optional #else directive.
Example:
<?xml version="1.0"?>
<templates>
<template name="resultList" parameters="results">
#for( var i = 0 ; i < results.length; i++ )
<p>
<a href="${results[i].url}">${results[i].title}</a><br/>
${results[i].content}<br/>
<span class="url">${results[i].url}</span>
#if( results[i].cacheUrl != null )
- <a class="cached" href="${results[i].cacheUrl}">Cached </a>
#else
<!-- no cached result associated with this search result -->
#end
</p>
#end
</template>
</templates>
#include
The #include directive allows for inclusion of sub templates defined in the same .miro file.
Example:
<?xml version="1.0"?>
<templates>
<template name="resultList" parameters="results">
#for( var i = 0 ; i < results.length; i++ )
#include item( results[ i ] )
#end
</template>
<template name="item" parameters="aResult">
<p>
<a href="${aResult.url}">${aResult.title}</a><br/>
${aResult.content}<br/>
<span class="url">${aResult.url}</span>
#if( aResult.cacheUrl != null )
- <a class="cached" href="${aResult.cacheUrl}">Cached</a>
#end
</p>
</template>
</templates>
${ expr }
The ${ expr } allows you to print the outcome of an expression into the HTML stream. expr can be any Javascript expression.
Example:
<?xml version="1.0"?>
<templates>
<template name="resultList" parameters="results">
#for( var i = 0 ; i < results.length; i++ )
<p>
<a href="${results[i].url}">${results[i].title}</a><br/>
${results[i].content}<br/>
<span class="url">${results[i].url}</span>
#if( results[i].cacheUrl != null )
- <a class="cached" href="${results[i].cacheUrl}">Cached </a>
#end
</p>
#end
</template>
</templates>
##
In some cases, there are needs to define local variables, functions and perform computation. The ## directive allows you to embed arbitrary Javascript code snippets which will be executed every time the template is called.
Example:
<?xml version="1.0"?>
<templates>
<template name="resultList" parameters="results">
#for( var i = 0 ; i < results.length; i++ )
##
var temp = "Title is:" + results[i].title;
##
<p>
<a href="${results[i].url}">${temp }</a><br/>
${results[i].content}<br/>
<span class="url">${results[i].url}</span>
#if( results[i].cacheUrl != null )
- <a class="cached" href="${results[i].cacheUrl}">Cached </a>
#end
</p>
#end
</template>
</templates>
Feedback and Comments
If you have comments or suggestions, shoot us an email at