Everything tagged html (2 posts)

Jaml: beautiful HTML generation for JavaScript

Generating HTML with JavaScript has always been ugly. Hella ugly. It usually involves writing streams of hard-to-maintain code which just concatenates a bunch of strings together and spits them out in an ugly mess.

Wouldn't it be awesome if we could do something pretty like this:

div(
h1("Some title"),
p("Some exciting paragraph text"),
br(),

ul(
li("First item"),
li("Second item"),
li("Third item")
)
);
div(
h1("Some title"),
p("Some exciting paragraph text"),
br(),

ul(
li("First item"),
li("Second item"),
li("Third item")
)
);

And have it output something beautiful like this:

<div>
<h1>Some title</h1>
<p>Some exciting paragraph text</p>
<br />
<ul>
<li>First item</li>
<li>Second item</li>
<li>Third item</li>
</ul>
</div>
<div>
<h1>Some title</h1>
<p>Some exciting paragraph text</p>
<br />
<ul>
<li>First item</li>
<li>Second item</li>
<li>Third item</li>
</ul>
</div>

With Jaml, we can do exactly that. Jaml is a simple library inspired by the excellent Haml library for Ruby. It works by first defining a template using an intuitive set of tag functions, and then rendering it to appear as pretty HTML. Here's an example of how we'd do that with the template above:

Jaml.register('simple', function() {
div(
h1("Some title"),
p("Some exciting paragraph text"),
br(),

ul(
li("First item"),
li("Second item"),
li("Third item")
)
);
});

Jaml.render('simple');
Jaml.register('simple', function() {
div(
h1("Some title"),
p("Some exciting paragraph text"),
br(),

ul(
li("First item"),
li("Second item"),
li("Third item")
)
);
});

Jaml.render('simple');

All we need to do is call Jaml.register with a template name and the template source. Jaml then stores this for later use, allowing us to render it later using Jaml.render(). Rendering with Jaml gives us the nicely formatted, indented HTML displayed above.

So we've got a nice way of specifying reusable templates and then rendering them prettily, but we can do more. Usually we want to inject some data into our template before rendering it - like this:

Jaml.register('product', function(product) {
div({cls: 'product'},
h1(product.title),

p(product.description),

img({src: product.thumbUrl}),
a({href: product.imageUrl}, 'View larger image'),

form(
label({'for': 'quantity'}, "Quantity"),
input({type: 'text', name: 'quantity', id: 'quantity', value: 1}),

input({type: 'submit', value: 'Add to Cart'})
)
);
});
Jaml.register('product', function(product) {
div({cls: 'product'},
h1(product.title),

p(product.description),

img({src: product.thumbUrl}),
a({href: product.imageUrl}, 'View larger image'),

form(
label({'for': 'quantity'}, "Quantity"),
input({type: 'text', name: 'quantity', id: 'quantity', value: 1}),

input({type: 'submit', value: 'Add to Cart'})
)
);
});

In this example our template takes an argument, which we've called product. We could have called this anything, but in this case the template is for a product in an ecommerce store so product makes sense. Inside our template we have access to the product variable, and can output data from it.

Let's render it with a Product from our database:

//this is the product we will be rendering
var bsg = {
title : 'Battlestar Galactica DVDs',
thumbUrl : 'thumbnail.png',
imageUrl : 'image.png',
description: 'Best. Show. Evar.'
};

Jaml.render('product', bsg);
//this is the product we will be rendering
var bsg = {
title : 'Battlestar Galactica DVDs',
thumbUrl : 'thumbnail.png',
imageUrl : 'image.png',
description: 'Best. Show. Evar.'
};

Jaml.render('product', bsg);

The output from rendering this template with the product looks like this:

<div class="product">
<h1>Battlestar Galactica DVDs</h1>
<p>Best. Show. Evar.</p>
<img src="thumbnail.png" />
<a href="image.png">View larger image</a>
<form>
<label for="quantity">Quantity</label>
<input type="text" name="quantity" id="quantity" value="1"></input>
<input type="submit" value="Add to Cart"></input>
</form>
</div>
<div class="product">
<h1>Battlestar Galactica DVDs</h1>
<p>Best. Show. Evar.</p>
<img src="thumbnail.png" />
<a href="image.png">View larger image</a>
<form>
<label for="quantity">Quantity</label>
<input type="text" name="quantity" id="quantity" value="1"></input>
<input type="submit" value="Add to Cart"></input>
</form>
</div>

Cool - we've got an object oriented declaration of an HTML template which is cleanly separated from our data. How about we define another template, this time for a category which will contain our products:

Jaml.register('category', function(category) {
div({cls: 'category'},
h1(category.name),
p(category.products.length + " products in this category:"),

div({cls: 'products'},
Jaml.render('product', category.products)
)
);
});
Jaml.register('category', function(category) {
div({cls: 'category'},
h1(category.name),
p(category.products.length + " products in this category:"),

div({cls: 'products'},
Jaml.render('product', category.products)
)
);
});

Our category template references our product template, achieving something rather like a partial in Ruby on Rails. This obviously allows us to keep our templates DRY and to easily render a hypothetical Category page like this:

//here's a second product
var snowWhite = {
title : 'Snow White',
description: 'not so great actually',
thumbUrl : 'thumbnail.png',
imageUrl : 'image.png'
};

//and a category
var category = {
name : 'Doovde',
products: [bsg, snowWhite]
}

Jaml.render('category', category);
//here's a second product
var snowWhite = {
title : 'Snow White',
description: 'not so great actually',
thumbUrl : 'thumbnail.png',
imageUrl : 'image.png'
};

//and a category
var category = {
name : 'Doovde',
products: [bsg, snowWhite]
}

Jaml.render('category', category);

All we've done is render the 'category' template with our 'Doovde' category, which contains an array of products. These were passed into the 'product' template to produce the following output:

<div class="category">
<h1>Doovde</h1>
<p>2 products in this category:</p>
<div class="products"><div class="product">
<h1>Battlestar Galactica DVDs</h1>
<p>Best. Show. Evar.</p>
<img src="thumbnail.png" />
<a href="image.png">View larger image</a>
<form>
<label for="quantity">Quantity</label>
<input type="text" name="quantity" id="quantity" value="1"></input>
<input type="submit" value="Add to Cart"></input>
</form>
</div>
<div class="product">
<h1>Snow White</h1>
<p>not so great actually</p>
<img src="thumbnail.png" />
<a href="image.png">View larger image</a>
<form>
<label for="quantity">Quantity</label>
<input type="text" name="quantity" id="quantity" value="1"></input>
<input type="submit" value="Add to Cart"></input>
</form>
</div>
</div>
</div>
<div class="category">
<h1>Doovde</h1>
<p>2 products in this category:</p>
<div class="products"><div class="product">
<h1>Battlestar Galactica DVDs</h1>
<p>Best. Show. Evar.</p>
<img src="thumbnail.png" />
<a href="image.png">View larger image</a>
<form>
<label for="quantity">Quantity</label>
<input type="text" name="quantity" id="quantity" value="1"></input>
<input type="submit" value="Add to Cart"></input>
</form>
</div>
<div class="product">
<h1>Snow White</h1>
<p>not so great actually</p>
<img src="thumbnail.png" />
<a href="image.png">View larger image</a>
<form>
<label for="quantity">Quantity</label>
<input type="text" name="quantity" id="quantity" value="1"></input>
<input type="submit" value="Add to Cart"></input>
</form>
</div>
</div>
</div>

You can see live examples of all of the above at http://edspencer.github.com/jaml.

Jaml currently sports a few hacks and is not particularly efficient. It is presented as a proof of concept, though all the output above is true output from the library. As always, all of the code is up on Github, and contributions are welcome :)

Jaml would be suitable for emulating a Rails-style directory structure inside a server side JavaScript framework - each Jaml template could occupy its own file, with the template name coming from the file name. This is roughly how Rails and other MVC frameworks work currently, and it eliminates the need for the Jaml.register lines. Alternatively, the templates could still be stored server side and simply pulled down and evaluated for client side rendering.

Happy rendering!

Continue reading

Custom containers with ExtJS

ExtJS has several built-in Container classes - classes which can contain one or more other Ext.Components (such as Grids, Forms, other Panels, etc). The most obvious example of a Container is the Ext.Panel class, along with its subclasses such as Ext.TabPanel, Ext.form.FormPanel and Ext.Window. With each container class you can add a bunch of components, like this:

//a child component to be added to the container below
var myComponent = new Ext.Panel({html: 'component 1'});

//Ext.Panel is a subclass of Ext.Container
var myPanel = new Ext.Panel({
items: [
myComponent,
{html: 'component 2'},
{html: 'component 3'}
]
});
//a child component to be added to the container below
var myComponent = new Ext.Panel({html: 'component 1'});

//Ext.Panel is a subclass of Ext.Container
var myPanel = new Ext.Panel({
items: [
myComponent,
{html: 'component 2'},
{html: 'component 3'}
]
});

Which will just create a Panel with three other Panels as its child components ('panel' is the default xtype, so we don't have to specify it). More to the point, you can add and remove components from the Container like this:

myPanel.add({html: 'component 4'});
myPanel.remove(myComponent);
myPanel.add({html: 'component 4'});
myPanel.remove(myComponent);

As myPanel is an Ext.Container subclass, the methods add() and remove() automatically add or remove child components from within the Container, and take care of any rendering that needs to be performed. Most of the time this is great, but what if you want to write your own custom Container? Say you had a bunch of shortcut links which performed some action in your application, and for styling or other reasons you want to put them into markup like this:

<div class="x-shortcuts-wrapper">
<div class="x-shortcuts-header"></div>
<div class="x-shortcuts">
<!-- child components to go here -->
</div>
<div class="x-shortcuts-footer"></div>
<button class="x-shortcuts-add">Add</button>
</div>
<div class="x-shortcuts-wrapper">
<div class="x-shortcuts-header"></div>
<div class="x-shortcuts">
<!-- child components to go here -->
</div>
<div class="x-shortcuts-footer"></div>
<button class="x-shortcuts-add">Add</button>
</div>

You might write something like this:

Ext.ns('MyApp');
/**
* @class MyApp.Shortcuts
* @extends Ext.Container
* Container for application shortcuts
*/
MyApp.Shortcuts = Ext.extend(Ext.Container, {
/**
* Creates the HTML markup for the shortcuts container
* @param {Ext.Container} ct The container into which this container will be rendered
*/
onRender: function(ct) {
this.el = ct.createChild({
cls: 'x-shortcuts-wrapper',
children: [
{cls: 'x-shortcuts-header'},
{cls: 'x-shortcuts'},
{cls: 'x-shortcuts-footer'},
{cls: 'x-shortcuts-add', tag: 'button'}
]
});

MyApp.Shortcuts.superclass.onRender.apply(this, arguments);

this.shortcutsHolder = this.el.child('.x-shortcuts');
},

//tells the container which element to add child components into
getLayoutTarget: function() {
return this.shortcutsHolder;
}
});
Ext.ns('MyApp');
/**
* @class MyApp.Shortcuts
* @extends Ext.Container
* Container for application shortcuts
*/
MyApp.Shortcuts = Ext.extend(Ext.Container, {
/**
* Creates the HTML markup for the shortcuts container
* @param {Ext.Container} ct The container into which this container will be rendered
*/
onRender: function(ct) {
this.el = ct.createChild({
cls: 'x-shortcuts-wrapper',
children: [
{cls: 'x-shortcuts-header'},
{cls: 'x-shortcuts'},
{cls: 'x-shortcuts-footer'},
{cls: 'x-shortcuts-add', tag: 'button'}
]
});

MyApp.Shortcuts.superclass.onRender.apply(this, arguments);

this.shortcutsHolder = this.el.child('.x-shortcuts');
},

//tells the container which element to add child components into
getLayoutTarget: function() {
return this.shortcutsHolder;
}
});

So our onRender method is responsible for creating some markup, which must be assigned to this.el. We're also calling the onRender() function of the superclass (Ext.Container) to make sure nothing is missed out.

The critical elements here are the getLayoutTarget() function, and the last line on onRender(). Usually when you subclass Ext.Container, the add() and remove() functions add and remove from this.el, which would result in something like this:

<div class="x-shortcuts-wrapper">
<div class="x-shortcuts-header"></div>
<div class="x-shortcuts"></div>
<div class="x-shortcuts-footer"></div>
<button class="x-shortcuts-add">Add</button>
<!-- child components will end up here -->
</div>
<div class="x-shortcuts-wrapper">
<div class="x-shortcuts-header"></div>
<div class="x-shortcuts"></div>
<div class="x-shortcuts-footer"></div>
<button class="x-shortcuts-add">Add</button>
<!-- child components will end up here -->
</div>

To prevent this from happening, we obtain a reference to the element we want components to actually be rendered to, and return that with getLayoutTarget(). After that the Container will once again do your bidding.

As of the time of writing getLayoutTarget() is not to be found anywhere in the Ext documentation (version 2.2), so my thanks go to Condor and Animal for answering my question on the ExtJS forum thread.

To round off the example, say your Shortcut class looked something like this:


/**
* @class MyApp.Shortcut
* @extends Ext.Component
* Clickable shortcut class which renders some HTML for a standard application shortcut
*/
MyApp.Shortcut = function(config) {
var config = config || {};

//apply some defaults
Ext.applyIf(config, {
text: 'Shortcut Name',
icon: 'default_shortcut.gif'
});

//call the superclass constructor
MyApp.Shortcut.superclass.constructor.call(this, config);
};
Ext.extend(MyApp.Shortcut, Ext.Component, {
onRender: function(ct) {
this.el = ct.createChild({
cls: 'x-shortcut',
children: [
{
tag: 'img',
src: this.initialConfig.icon
},
{
tag: 'span',
html: this.initialConfig.text
}
]
});

MyApp.Shortcut.superclass.onRender.apply(this, arguments);
}
});

Ext.reg('shortcut', MyApp.Shortcut);

/**
* @class MyApp.Shortcut
* @extends Ext.Component
* Clickable shortcut class which renders some HTML for a standard application shortcut
*/
MyApp.Shortcut = function(config) {
var config = config || {};

//apply some defaults
Ext.applyIf(config, {
text: 'Shortcut Name',
icon: 'default_shortcut.gif'
});

//call the superclass constructor
MyApp.Shortcut.superclass.constructor.call(this, config);
};
Ext.extend(MyApp.Shortcut, Ext.Component, {
onRender: function(ct) {
this.el = ct.createChild({
cls: 'x-shortcut',
children: [
{
tag: 'img',
src: this.initialConfig.icon
},
{
tag: 'span',
html: this.initialConfig.text
}
]
});

MyApp.Shortcut.superclass.onRender.apply(this, arguments);
}
});

Ext.reg('shortcut', MyApp.Shortcut);

Then our container would be created like this:


new MyApp.Shortcuts({
items: [
new MyApp.Shortcut({text: 'Shortcut 1', icon: 'shatner.gif'}),
{xtype: 'shortcut', text: 'Shortcut 2', icon: 'nimoy.gif'},
{xtype: 'shortcut'}
]
});

new MyApp.Shortcuts({
items: [
new MyApp.Shortcut({text: 'Shortcut 1', icon: 'shatner.gif'}),
{xtype: 'shortcut', text: 'Shortcut 2', icon: 'nimoy.gif'},
{xtype: 'shortcut'}
]
});

Which would produce HTML like this:

<div class="x-shortcuts-wrapper">
<div class="x-shortcuts-header"></div>
<div class="x-shortcuts">
<div class="x-shortcut">
<img src="shatner.gif" />
<span>Shortcut 1</span>
</div>
<div class="x-shortcut">
<img src="nimoy.gif" />
<span>Shortcut 2</span>
</div>
<div class="x-shortcut">
<img src="default_shortcut.gif" />
<span>Shortcut Name</span>
</div>
</div>
<div class="x-shortcuts-footer"></div>
<button class="x-shortcuts-add">Add</button>
</div>
<div class="x-shortcuts-wrapper">
<div class="x-shortcuts-header"></div>
<div class="x-shortcuts">
<div class="x-shortcut">
<img src="shatner.gif" />
<span>Shortcut 1</span>
</div>
<div class="x-shortcut">
<img src="nimoy.gif" />
<span>Shortcut 2</span>
</div>
<div class="x-shortcut">
<img src="default_shortcut.gif" />
<span>Shortcut Name</span>
</div>
</div>
<div class="x-shortcuts-footer"></div>
<button class="x-shortcuts-add">Add</button>
</div>
Continue reading