Friday, May 2, 2008

JavaScript Templates: A major leap on the way of Web2.0

An undisputed fact is that JavaScript acts as a cornerstone to Web 2.0 application development. Web 2.0 applications extensively rely on JavaScript logic to manipulate DOM elements on the fly. let's consider a typical example. A JavaScript function is needed to generate some HTML fragment and insert it into a placeholder that already exists in the document. Let's say that the resulting fragment represents some user's data and that it is required to look like this:

<img src='/users/12/image' title='Haitham Mohammad' alt='Haitham Mohammad' />
<label class='user_label'> Name: </label>
<span class='user_name'> Haitham Mohammad </span>
<label class='user_label'> Telephone: </label>
<span class='user_telephone'> 0127038199 </span>

Now let's see how that JavaScript function would look like: (I know it's not minimal, but it's a typical example. let's say that it will, at least, look like that)

showUserDetails = function(user){
placeHolder = document.getElementById('place_holder');

img = document.createElement
img = document.createElement('img');
img.src = user.imageSource;
img.title = user.name;
img.alt = user.name;
placeHolder.appendChild(img);

nameLabel = document.createElement('label');
nameLabel.className = 'user_label';
nameLabel.innerHTML = 'Name: ';
placeHolder.appendChild(nameLabel);

nameSpan = document.createElement('span');
nameSpan.className = 'user_name';
nameSpan.innerHTML = user.name;
placeHolder.appendChild(nameSpan);

telLabel = document.createElement('label');
telLabel.className = 'user_label';
telLabel.innerHTML = 'Telephone: ';
placeHolder.appendChild(telLabel);

telSpan = document.createElement('span');
telSpan.className = 'user_telephone';
telSpan.innerHTML = user.telephone;
placeHolder.appendChild(telSpan);
}

Yes, I know, it's long and ugly. However, this is what I used to do in my early couple of months in JavaScript. Now let's formulate the problem that lead to this long and ugly code. The problem is that HTML is a descriptive language while JavaScript is a procedural one. The anomaly appeared when we tried to 'describe' our HTML fragment using JavaScript 'procedure'. i.e. When we tried to override the descriptive nature of HTML using the procedural nature of JavaScript.

Now let's be a bit positive. The solution simply exists in a relatively new open source JavaScript library called JavaScript Templates (or JST), developed by TrimPath . A JavaScript Template is an HTML template that is yet to be evaluated using some JavaScript variables. This principle is so close to all server-side templates like jsp, erb, ..etc.

Now, the new steps are: i) write the JST that DESCRIBES your data representation. ii)write the JavaScript function that EVALUATES that template some given JavaScript variables as data parameters. Let's apply to the example.

Performing step (i), Our JST will look like that:

<textarea id='jst_user_details' style='display: none;'>
<img src='${theUser.imageSource}' title='${theUser.name}'
alt='${theUser.name}' />
<label class='user_label'> Name: </label>
<span class='user_name'> ${theUser.name} </span>
<label class='user_label'> Telephone: </label>
<span class='user_telephone'> ${theUser.telephone} </span>
</textarea>

We hold the template code inside a hidden textarea with a known id just to be able to get the template afterwards at runtime. The evaluation sign '${}' means that what's inside the curly braces is still to be evaluated afterwards at runtime.

Now comes the magic in step (ii). Our long and ugly JavaScript function will be replaced with this one:

showUserDetails = function(user){
userHTML = TrimPath.processDOMTemplate('jst_user_details',
{ theUser: user } );
placeHolder = document.getElementById('place_holder');
placeHolder.innerHTML = userHTML;
}

We simply called a function that processes the JST contained inside the textarea with id = 'jst_user_details', given one data variable named 'theUser' with the same value as the parameter 'user'. And, tadaa, we have a pure HTML fragment that describes a certain user's details.

JST also supports basic functionality of any server-side template language, like repitition and conditionals. A JST can typically contain the following snippet:

{for item in itemss}
{if user.admin}
<input type='button' value='edit' ...
{else}
<input type='button' value='view' ...
{/if}
{/for}

To sum it up, what JST does is eliminating the problem we introduced before. It simply separates HTML description from JavaScript procedures. Talking about myself, It made me at least four times as productive as I used to be using traditional DOM manipulations. Besides, your code becomes more neat, more modular, and more organized. Go ahead and give it a try: http://code.google.com/p/trimpath/wiki/JavaScriptTemplates


No comments: