Geeks With Blogs
.NET Nomad What I've learned along the way

Whenever I am working on a project that requires client-side coding, I immediately starting thinking in JQuery.  There are other javascript frameworks out there, but so far I haven’t found one that allowed me to structure my client-side applications quite as cleanly as JQuery does.

The only thing JQuery doesn’t support out-of-the-box is a decent templating system for emitting DOM elements.  Typically you’d wind up building up large strings and passing them to a method like append, html, etc in order to attach them to the DOM.  There are also numerous plug-ins that allow you to structure tags using plain javascript objects.  Aside from being tedious, this method is highly error prone and difficult to debug.  Further, it doesn’t really let you separate your markup from your code, the two things become quickly linked and you wind up with a very messy application.

Recently, I found the Tempest plugin written by Nick Fitzgerald.  It takes a simple, but powerful approach to templatng using JQuery.  The documentation provided on the plugin’s page is pretty straight forward and I encourage you to go read it before continuing on with this article.

The iter Tag

One of the things I really liked about Tempest was that it allows you to implement your own custom tags that can be called inside of a template.  The number of uses for this is infinite, and it is even how Tempest defines its own if tag.

Very early on in my use of Tempest I ran across a scenario that prompted me to create my own tag.  I had an array of javascript objects similar to the following:

[{ tid: '123', team_name: 'Team A', roster: [{ name: 'Sal' }, { name: 'Bob'}] },
 { tid: '321', team_name: 'Team B', roster: [{ name: 'Sally' }, { name: 'Linda' }, { name: 'Thelma'}]}]

As you can see, each object in the collection contains a member called “roster” which is itself a collection of complex objects.  The difficulty I ran into was that I wanted to make the following call to Tempest:

$(function() {
    var teams = [{ tid: '123', team_name: 'Team A', roster: [{ name: 'Sal' }, { name: 'Bob'}] },
                 { tid: '321', team_name: 'Team B', roster: [{ name: 'Sally' }, { name: 'Linda' }, { name: 'Thelma'}]}];
                        
    $('#teams').html($.tempest('team-list', teams));
});

Now, Tempest is smart enough to know that I’ve passed it an array of objects and is therefore going to apply the “team-list” template to each of the elements in the “teams” array.  However, there is no way to tell the template that it needs to iterate over each element in the “roster” array.  This lead me to define the following custom tag:

$.tempest.tags.iter = {
 expectsEndTag: false,
 render: function(context) {
     return $('<span>').append($.tempest(
         this.args[0],
         context[this.args[1]]
     ).clone()).html();
 }
};

In order to call this tag inside of a template I would use the following syntax:

{% iter roster-members roster %}

Where iter is the name of my custom tag, “roster-members” is the name of the Tempest template I want to apply, and “roster” is the name of the property on the context object that contains my list of elements.

What happens here is that when the Tempest template engine see my custom tag, it executes the “render” method that I’ve defined passing it a context object (in this case an element from the teams array).  The template engine will also populate an array called “args” that contains each argument that I’ve passed in my call to the custom tag.

The iter tag’s render method expects the first argument (i.e. args[0]) to be the name of a pre-defined Tempest template.  The second argument (i.e. args[1]) is expected to be the name of an attribute on the context object that contain the collection it is to iterate over.

The “render” method of any Tempest custom tag is required to be a string.  In the case of iter, this string is the result of calling tempest to format some data using a pre-defined template.  The rest of the code wrapping the call to tempest is simply some JQuery hackery that lets us get the entire string of HTML that we are going to return.

 

An Example of using iter

The following is a complete example of using the iter tag as defined above.  If you find yourself using Tempest as much as I have, you would be well advised to create a stand alone Javascript file called “tempesttags.js” in which you define your own library of custome tempest tags.  Include this file after the tempest plugin itself and you should be able to share them from project to project.

 

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" >
<head>
    <title>Tempest Example</title>
    <script type="text/javascript" language="javascript" src="/Scripts/jquery-1.3.2.js"></script>
    <script type="text/javascript" language="javascript" src="/Scripts/jquery.tempest-0.3.1.min.js"></script>
</head>
<body>
    <script type="text/javascript" language="javascript">

        $(function() {

            $.tempest.tags.iter = {
                expectsEndTag: false,
                render: function(context) {
                    return $('<span>').append($.tempest(
                        this.args[0],
                        context[this.args[1]]
                    ).clone()).html();
                }
            };

            var data = [{ tid: '123', team_name: 'Team A', roster: [{ name: 'Sal' }, { name: 'Bob'}] },
                        { tid: '321', team_name: 'Team B', roster: [{ name: 'Sally' }, { name: 'Linda' }, { name: 'Thelma'}]}];

            $('div#team-holder').html($.tempest('team-details', data));
        });
    </script>
    
    <textarea title="team-details" class="tempest-template" style="display: none;" cols="0">
        <div id="{{ tid }}" class="team-details">
            <span>{{ team_name }}</span>  
            <ul>
                {% iter roster-member roster %}
            </ul>          
        </div>
    </textarea>
    
    <textarea title="roster-member" class="tempest-template" style="display: none;" cols="0">
        <li>{{ name }}</li>
    </textarea>
    
    <div id="team-holder"></div>
</body>
</html>
Posted on Sunday, February 7, 2010 1:01 PM | Back to top


Comments on this post: Client-Side Templating with JQuery and Tempest

No comments posted yet.
Your comment:
 (will show your gravatar)


Copyright © newman | Powered by: GeeksWithBlogs.net