Alloy - Template Demo

AlloyUI Templates allow the ability to format text and JavaScript objects in an easy to use manner that is also performant.

Simple formatting

Template markup

<div class="user-name">Name: {name}</div>
<div class="user-expertise">Expertise: {expertise}</div>
					

Javascript object

{name: 'Nate Cavanaugh', expertise: 'Procrastinating'}
					

Looping

Loops can be nested, as well as operate on Arrays or Objects. If looping over an array, use the {.} variable to refer to the current item in the loop. If looping over an object, you can use the keys of the object such as {name} (if obj.name exists) or {age} (if obj.age exists), etc.

Template markup

<div class="user-name">{name}</div>
<ul class="user-faves">
	<tpl for="faves">
		<li class="fav">{.}</li>
	</tpl>
</ul>
					

Javascript object

{
	name: 'Eduardo Lundgren',
	faves: ['Dulce de leche', 'Liferay', 'Acai', 'Haagen Daaz', 'JavaScript']
}
					

Conditionals

You can test conditions using an if attribute of the tpl element.

Template markup

<tpl for=".">
	<p>
	{name} current timezone is GMT
	<tpl if="residence == 'Madrid'">
	+2.
	</tpl>
	<tpl if="residence == 'Varna'">
	+3.
	</tpl>
	<tpl if="residence == 'Olinda'">
	-2.
	</tpl>
	</p>
</tpl>
					

Javascript object

[
	{name: 'Iliyan Peychev', residence: 'Madrid'},
	{name: 'Bruno Basto', residence: 'Olinda'}
]
					

Formatting

You have a couple of different formatting options available to you. To use the baked in formatting methods (which are all contained on A.Lang.String), you simply need to format your variable like so: {var:formattingMethod}. An example of this would be to capitalize the name variable, you would do: {name:capitalize}, or to repeat the age variable 10 times, you could do {age:repeat(10)}. You can also add your own methods on a per template instance by passing the last configuration item into the A.Template constructor as an object that contains methods. For example, new A.Template('{name:customFormatter}', {customFormatter: function(str){return str;}})

* The formatting methods can take any number of arguments, but the first argument is always implicitly passed, which is the string itself. So if you have no arguments, you don't need to use the parenthesis. If you do have other arguments, you need the parenthesis, but you don't need to pass the string itself.

Template markup

<tpl for=".">
	<p><b>{title:toUpperCase}</b><br />
		by <i>{author}</i><br />
		{description:truncate(100)}
	</p>
</tpl>
					

Javascript object

[
	{title: 'Mere Christianity', author: 'C.S. Lewis', description: 'In 1943 Great Britain, when hope and the moral fabric of society were threatened by the relentless inhumanity of global war, an Oxford don was invited to give a series of radio lectures addressing the central issues of Christianity. Over half a century after the original lectures, the topic retains it urgency. Expanded into book form, Mere Christianity never flinches as it sets out a rational basis for Christianity and builds an edifice of compassionate morality atop this foundation. As Mr. Lewis clearly demonstrates, Christianity is not a religion of flitting angels and blind faith, but of free will, an innate sense of justice and the grace of God. --This text refers to an out of print or unavailable edition of this title.'},
	{title: 'Dark Rivers of the Heart', author: 'Dean Koontz', description: 'Do you dare step through the red door? Spencer Grant had no idea what drew him to the bar with the red door. He thought he would just sit down, have a slow beer or two, and talk to a stranger. He couldn\'t know that it would lead to a narrow escape from a bungalow targeted by a SWAT team. Or that it would leave him a wanted man. Now he is on the run from mysterious and ruthless men. He is in love with a woman he knows next to nothing about. And he is hiding from a past he can\'t fully remember. On his trail is a shadowy security agency that answers to no one--including the U.S. government—and a man who considers himself a compassionate Angel of Death. But worst of all, Spencer Grant is on a collision course with inner demons he thought he\'d buried years ago—inner demons that could destroy him if his enemies don\'t first.'},
	{title: 'The Importance of Being Earnest', author: 'Oscar Wilde', description: 'The Importance of Being Earnest is an important play by Oscar Wilde, and is a comedy of manners that discusses the serious of society. Set in late Victorian England, the story is about the main charachter, John Worthing\'s, ficticious brother Ernest, which is the main source of the comedy in this work. This is an important play for those are fans of comedy plays and of course the works of Oscar Wilde.'},
	{title: 'Pygmalion', author: 'G. B. Shaw', description: 'An idealistic professor transforms an unsophisticated Cockney girl into a refined young lady in this classic drama set in turn-of-the-century London.'},
	{title: 'The Great Divorce', author: 'C. S. Lewis', description: 'What if anyone in Hell could take a bus trip to Heaven and stay there forever if they wanted to? In The Great Divorce C. S. Lewis again employs his formidable talent for fable and allegory. The writer finds himself in Hell boarding a bus bound for Heaven. The amazing opportunity is that anyone who wants to stay in Heaven, can. This is the starting point for an extraordinary meditation upon good and evil, grace and judgment. Lewis\'s revolutionary idea is the discovery that the gates of Hell are locked from the inside. In Lewis\'s own words, "If we insist on keeping Hell (or even earth) we shall not see Heaven: if we accept Heaven we shall not be able to retain even the smallest and most intimate souvenirs of Hell."'}
]
					

Rendering from an Element

You can place your template markup on the page in a couple of different formats. You can place it as a hidden div, inside of an input/textarea, or in a script element (if you use a script element, you MUST set the type="" attribute to something other than "text/javascript"; we use type="aui/template").

Template markup

Script tag

<script id="scriptTPL" type="aui/template">
	<tpl for=".">
		{#} {.}
	</tpl>
</script>

Hidden Div

<div class="aui-helper-hidden" id="divTPL">
	<tpl for=".">
		{#} {.}
	</tpl>
</div>

Hidden Textarea

<textarea class="aui-helper-hidden" id="textareaTPL">
	<tpl for=".">
		{#} {.}
	</tpl>
</textarea>

Javascript object

Script tag

var scriptTPL = A.one('#scriptTPL').toTPL();
scriptTPL.render(['Item 1', 'Item 2', 'Item 3'], '#somewhere');

Hidden Div

var divTPL = A.one('#divTPL').toTPL();
divTPL.render(['Item 1', 'Item 2', 'Item 3'], '#somewhere');

Hidden Textarea

var textareaTPL = A.one('#textareaTPL').toTPL();
textareaTPL.render(['Item 1', 'Item 2', 'Item 3'], '#somewhere');

Embedding JavaScript

You can embed arbitrary JS code into your templates using scriptlets. The result of the scriptlet expression is embedded into the template. A scriptlet is demarked with the special {[ syntax ]}. Scriptlets can accept any valid JS expression, but are also passed the following variables:

  • values - This is the current object that is being applied to the template
  • Any of the current template variables - so {name} would be available as values.name, {skills} as values.skills, etc
  • parent - This is the object of the parent scope (for instance, when running in a loop, you can access the values of the parent scope)
If you need to do more advanced logic, consider placing the logic into a method on your template (which you can access like so: this.myMethodName()) * One thing to note, accessing or manipulating the DOM of the template is not allowed, as the JS is executing before the template has finished rendering. You can modify/read other parts of the DOM and have that value returned in your expression however.

Template markup

<tpl for=".">
	<div class="scriptlet-row" style="background-color: {[$index % 2 === 0 ? '#98C0F4' : '#DFE8F6']};">
		{name} -  {[ A.getClassName(values.name, 'styled', 'element') ]}
	</div>
</tpl>

Javascript object

[
	{name: 'Ollie', age: 18},
	{name: 'Kevin', age: 15},
	{name: 'Arnie', age: 14},
	{name: 'Alice', age: 22}
]

Advanced Looping

Loops contain other special variables that help with processing logic of the data you're dealing with. The variables are:

  • {.} - A shortcut to the current item itself
  • {#} - A shortcut to the $index variable
  • {$index} - This is the current number of the item in the loop, however it's 1-based, instead of 0-based like normal javascript arrays (also available in scriptlets as $index)
  • {$i} - This is the current 0-based number of the item in the loop (also available in scriptlets as $i)
  • {$count} - This is the total number of items of the array (also available in scriptlets as $count)
  • {$last} - This is a true or false value indicating whether you are currently on the last item of the array (also available in scriptlets as $last)

Template markup

<tpl for="devs">
	{#}. {.}<tpl if="!$last">,</tpl>
</tpl>

Javascript object

{devs: ['Nate', 'Eduardo', 'Iliyan', 'Bruno', 'Jon', 'Aaron']}

Performance

Parsing a template is extremely fast. Because of the way it's compiled down, the end call is just basic string substitution. This test creates an object using 5,000 rows and parses it out.

* There is one small cheat we do in this test, which is we move the container off DOM first. Modifying a live DOM element is always the most expensive part. For a ratio of comparison, for 5,000 rows, calling tpl.parse({...5,000...}) takes only about 5ms but the rendering portion (if done on DOM) takes about 300ms.

Template markup

<tpl for="rows">
	<div style="background-color: {[ $i % 2 ? '#ccc' : '']}; border-bottom: {[ !$last ? '1px solid #555' : '']}">{#}. {.}</div>
</tpl>

Javascript object

(function(){
	var obj = {rows: []}, rows = obj.rows;
	var data = ['Nate', 'Eduardo', 'Iliyan', 'Bruno', 'Jon', 'Aaron'];
	var getRandomString = function(i){
		var seed = 2147483648 * i;
		  return Math.floor(Math.random() * seed).toString(36) + Math.abs(Math.floor(Math.random() * seed) ^ A.Lang.now()).toString(36);
	}

	for(var i = 0; i < 5000; i++) {
		rows[rows.length] = getRandomString(i);
	};
	return obj;
})();

Examples

There are a couple of different ways in which you can create a template.
The A.Template constructor can be passed either an array of strings as the first argument, or as just a series of strings.

Creating a new Template

tpl = new A.Template('This is my', '{name}.');
or
tpl = new A.Template(['And this is my', '{age}.']);

Making extra methods available to the template

tpl = new A.Template('This is my {age:getMax(10)}.', {
	getMax: function(value, max){
		return Math.max(value, max);
	}
});

Getting a template from a node

tpl = A.Template.from('#divContainingTplMarkup');
or
tpl = A.one('#divContainingTplMarkup').toTPL();

Rendering a template to a node

tpl.render({name: 'Nate', age: 31}, '#outputContainer');
or
A.one('#outputContainer').renderTPL(['This is my name: {name}'], {name: 'Nate', age: 31});

Parse a template to a string without rendering

html = tpl.parse({name: 'Nate', age: 31});
From Div

{name}:{age} - {age * 5}