Templating is what makes writing large applications for the web viable.  Server side templating has been around for a while and typically allows you to have small snippets of code executed inside a text file to fill in parts of the text file that are blank.  JavaScript now offers many templating libraries which vary from sections of fully fledged JavaScript to the extremely compact yet very powerful syntax of Mustache.

Mustache can be found at https://github.com/janl/mustache.js

When rendering content using mustache you have 2 parts, the view and the template.  I’m going to walk you through a real world example of displaying the navigation structure of a web-site using mustache.

The View

Let’s create a view to model a list of links.  This code should be fairly simple to follow as it’s just like what you’ve been seeing up to this point.

//define a data model for the link data-type
function link(id, name) {
    //Use prototype in order to get the selected method.
    var that = Object.create(link.prototype);
    that.url = "#/" + id + "/";
    that.name = name;
    return that;
}
link.prototype.selected = function () {
    return this.url === location.hash;
}
//build an array of links
var links = [link("home", "Home"), link("edit", "Edit Page"), link("view", "View Stuff")];
//define a mustache view
var view = {links:links};

This produces a view which you can continually re-use each time you need to render the navigation.  The key thing to note about this is that it everything in it is about the actual state of things as data, and not about their visual appearance.

Template

Now let’s create a template to display the links.

{{#links}}
    <a href="{{url}}" {{#selected}}class="Selected"{{/selected}}>{{{name}}}</a>
{{/links}}

OK, loads of stuff happened in that last code snippet.  Let’s run through it one stage at a time.  The <a> tag should be familiar as an html tag that will display a link, but the curly braces are totally new and weird.

Anything wrapped in {{ and }} will be interpreted as being for Mustache to render.  The two simplest tags are {{url}} and {{name}}.  These are the bread and butter of Mustache and will simply be replaced directly with the appropriate text.

The tags beginning with a # character start blocks and the tags beginning / close the block of the same name.  The links block represents an enumerable block, while the selected block represents a conditional block.

The enumerable block takes an array from the view and enumerates each element in that array.  It then uses the block it contains as the template for each element in the array.  The conditional block prints out whatever’s within it if and only if the value is “truthy”.  Mustache is clever enough to execute functions and use the returned value.

Rendering a template

The template will be rendered something like this:

First consider the {{#links}} block.  Find the links property on the view and discover it contains an array.  Therefore repeat the following template for each element in the array.

<a href="{{url}}" {{#selected}}class="Selected"{{/selected}}>{{{name}}}</a>

The context for this template will be each element in the array. So for the first element it will replace {{url}} with #/home/ and {{name}} with Home then it will consider the block {{#selected}} Since this is returns function it will execute the function and because the function does not return an array it will be treated as a conditional block. If the link is selected it will return true.

Putting it all together

The code below uses what we’ve looked at so far on templating to render a simple web site with links you can click on to navigate between pages. Note that the script references both jquery and mustache.

<!doctype html>
<html lang="en">
<head>
    <meta charset="utf-8">
    <title>Articles</title>
    <style>
        nav
        {
            background-color: #FDC107;
            height: 50px;
        }

            nav a
            {
                -webkit-appearance: none;
                -webkit-border-radius: 0px;
                font: 14px Arial;
                text-align: center;
                border: none;
                height: 35px;
                display: block;
                float: left;
                text-decoration: none;
                padding-top: 15px;
                padding-left: 50px;
                padding-right: 50px;
                color: #FFFFFF;
            }

                nav a:hover, nav a.Selected
                {
                    background-color: #6699FF;
                    color: #000000;
                }
    </style>
</head>
<body>
    <nav>
    </nav>
    <article>
    </article>
    <script type="text/template" id="navTemplate">
    {{#links}}
        <a href="{{url}}" {{#selected}}class="Selected"{{/selected}}>{{{name}}}</a>
    {{/links}}
    </script>
    <script src="http://ajax.aspnetcdn.com/ajax/jQuery/jquery-1.7.1.min.js"></script>
    <script src="http://ajax.cdnjs.com/ajax/libs/mustache.js/0.3.0/mustache.min.js"></script>
    <script>
        $(function () {
            var view = (function () {
                //define a data model for the link data-type
                function link(id, name) {
                    //Use prototype in order to get the selected method.
                    var that = Object.create(link.prototype);
                    that.url = "#/" + id + "/";
                    that.name = name;
                    return that;
                }
                link.prototype.selected = function () {
                    return this.url === location.hash;
                }
                //build an array of links
                var links = [link("home", "Home"),
                    link("hello", "Hello World"),
                    link("article", "A really interesting article")];
                return { links: links };
            }());
            function onChange() {
                $("nav").html(Mustache.to_html($("#navTemplate").html(), view));
                switch (location.hash) {
                    case "#/home/":
                        $("article").html("Welcome to the home page," +
                            " click the links to see templating in action.");
                        break;
                    case "#/hello/":
                        $("article").html("Hello World is traditionally displayed" +
                            " as text for people's first programmes in a new language");
                        break;
                    case "#/article/":
                        $("article").html("This article is fascinating, honestly." +
                            "  In fact, it's an example of meta-humour.");
                        break;
                    default:
                        $("article").html("The page you looked for was not found");
                        break;
                }
            }
            location.hash = "#/home/";
            window.onhashchange = onChange;
            onChange();
        }());
</script>
</body>
</html>

The main thing to note here is that we put the template inside a script tag. The type of the script tag is important but can be anything the browser won’t interpret as code. Often you might want to set this to text/html as then more browsers will attempt to do syntax highlighting and indentation for you etc. However sometimes they will just report your template as invalid html (because it isn’t really html yet). The browser will then ignore this text and we can access it using jquery as $(“#navTemplate”).html(). We could store the template in any number of other ways. For example, we could have chosen to have it in a separate file and use ajax to download it.

Important things not used in the example

HTML Escaping and Negative Conditional Blocks are worth familiarising yourself with. If you find however that you need more power, mustache.js also has support for comments, partials, functions (which can perform advanced stuff on blocks which is really cool but fairly complex), {{.}} for accessing the current item in an array of strings.

HTML Escaping

By default all variables are html escaped. That means that if the user types in </br> it will be converted to &lt;/br&gt; so that when rendered in the browser it looks like </br> rather than creating a new line break. You can however avoid this escaping by using the triple mustache tag. It looks like this:

<div>
{{{content}}}
</div>

Normally you want the html escaping for user entered input as it prevents them entering something unexpected like scripts.

Negative Conditional Blocks

These are called inverted sections in the Mustache documentation. They allow you to have something happen when a condition is false. So we could make our template look like:

{{#links}}
    <a href="{{url}}" class="{{^selected}}un{{/selected}}selected">{{{name}}}</a>
{{/links}}
{{^links}}
    <p>There are no links to display :(</p>
{{/links}}

Which would give a helpful message when there were no links and give each link either a class of selected or unselected depending on whether or not they were currently selected.

Advertisements