Templating
A very common case is that we have a data structure and want to generate some HTML from it. This happens often enough that it's worth having a shortcut for generating HTML, called a “template.” As an example of this, we'll examine the Dust templating system (found at https://github.com/linkedin/dustjs).
Example
We already looked at how one can write JavaScript code using jQuery to implement simple forum. That example included the following function that creates HTML from some JSON received from the server:
function showPosts(jsonData) {
var oldposts = $('#oldposts');
oldposts.empty();
if (jsonData.ok) {
$.each(jsonData.posts, function (index, post) {
var newElt = $('<div>');
// SECURITY HOLE: Don't post raw HTML from user!
newElt.html('<b>User:</b> ' + post.name + '<br />\n'
+ '<div>' + post.message + '</div>');
oldposts.append(newElt);
});
} else {
oldposts.text(jsonData.message);
}
}
(Recall that jsonData
is an object with
a field named ok
that is true
or false
.
A value of true
indicates that the request was
successful, in which case jsonData
also has a field
named posts
that is an array of objects, each itself
having a name
and a message
.
But if ok
is false
, that indicates that the
request was unsuccessful, in which case jsonData
also
has a field named message
describing what went
wrong.)
Admittedly, this doesn't look too bad, but that's because the example was intentionally trimmed down to its minimum, with bare-bones HTML. But it's a tedious part of Web programming, and programmers have found that templating is much more convenient: We build a template that is basically HTML but with some “blanks” where variables should go, and we refer the JavaScript to that template instead.
There are a wide variety of templating options out there, but we'll use Dust, which is one of the more prominent solutions among those that work on the browser. Here's a Dust template for our problem.
{?ok}
{#posts}
<div>
<b>User:</b> {name}<br>
<div>{message}</div>
</div>
{/posts}
{:else}
{message}
{/ok}
Notice how this includes several pieces enclosed by braces: These are Dust-specific commands.
The
{?XX}
notation (as with{?ok}
) means to test whetherXX
is “truthy” — whetherXX
is defined and is not one of the following “falsy” values:false
,null
, empty string, or empty array. If it is “truthy”, the result of the template is everything from{?XX}
to{:else}
; if it is “falsy”, the result is everything from{:else}
to{/XX}
. (If{:else}
is omitted, it is assumed to be empty.)The
{#XX}
notation (as with{#posts}
) means to generate the HTML enclosed between{#XX}
and{/XX}
for each object in the listXX
. As we iterate through each object in the array, the “context” changes to that object, so references to variables inside will first look to the current array object, and if that is undefined it will look to the overall JSON object.The
{XX}
notation (as with{name}
) means to plug in the value ofXX
within the given context (or ifXX
is undefined, search the parent context).
Once we've created this template, Dust “compiles”
it into a named slice of JavaScript; we'll suppose we call our
template here “postlist
”, and we'll suppose
we've saved this JavaScript into a file named my-template.js
.
Our HTML file will need to load a Dust library along with this
generated JavaScript file from the server:
<script src="dust-core.js"></script>
<script src="my-template.js"></script>
Once that is done, we can use the template in showPosts
:
function showPosts(jsonData) {
dust.render('postlist', jsonData, function(err, out) {
if (err) console.log(err);
$('#oldposts').html(out || 'Error');
});
}
The first parameter to dust.render
is the Dust
template's name; the second is the “context” it
should use for finding any variables references in the template,
and the last is a callback function that is handed the HTML
generated by the template in the second parameter, out
.
Browser-side vs server-side
With Dust, the usage of the template occurs in the browser: the server sends us raw data in JSON format, and the browser itself applies the template to get the resulting HTML. (This is complicated somewhat by the fact that Dust templates are actually “compiled” into JavaScript. That allows Dust to have greater performance than if it tried to interpret the template directly.)
Whereas Dust uses this browser-side templating, other systems use server-side templating. In them, the server applies the template before sending anything to the browser. Consequently, the server doesn't send the raw JSON data — it sends the HTML, which the browser will simply paste into its tree.
Naturally, there are advantages and disadvantages of each. Personally, I think the advantages to browser-side templating are more obvious:
There is a clean separation between browser and server: The browser-side code handles all presentation-related code, whereas the server-side code simply fetches from the database and ships it to the browser in JSON format.
After the full page is loaded, all information is sent to the browser in JSON format, which is relatively compact.
Still, there are some advantages to server-side templating.
Server-side templating has been around longer, so the solutions are more established.
Server-side templating requires less code to be sent to the browser (no
dust-core.js
ormy-template.js
), so the browser can load the page faster.Server-side templating requires less browser-side computation, which is particularly useful for weak processors like those often found on smartphones.
Servers can apply caching to avoid re-applying templates.
Search engines generally don't execute JavaScript or JSON, but server-side templating can be indexed. (This isn't relevant to all Web sites, but a newspaper and shopping site typically want information from its database to be discoverable via search indexes.)
Even though I have a longer list of advantages to server-side templating, in general I would lean toward browser-side templating: It's nice to have all HTML responsibilities delegated to the browser-side code. Still, there are certainly cases where server-side templating makes more sense.