Pulling in an entire dollop of ready-to-go HTML via Ajax makes it nice and easy to stick
in your page, but typically this isn’t the data format you’ll receive from a web service or
API. This means it’s our responsibility to turn data into markup. Even if you’re in control
of the back end, it still makes sense to transmit your site data in XML or JSON rather than
HTML, since those files will usually be much smaller, and require less of your mobile users’
precious bandwidth.
So, how will this work? We start by receiving a list of data from the server. For each
item in the list, we want to create an HTML fragment (such as a list item) and inject some
values from the data. Finally, we want to add each fragment into the correct place in the
page. There are a few common ways to approach this situation:
build the HTML fragments ourselves, in JavaScript
duplicate a “template” DOM element and change the contents of the relevant
tags
use a templating engine
We’ll quickly visit each approach and see what works best in which contexts. We’ll be
assuming our data source is in the JSON format, and looks a little like this:
listing 1. data/spots.json (excerpt)
[{
"id": 4,
"name": "Planet Bollywood",
"distance": "400m",
"sightings": 10
}, {
"id": 7,
"name": "Soft Rock Café",
"distance": "1.1Km",
"sightings": 3
}]
|
It’s a simple array, with each element consisting of an object containing
details about locations. This is a very common (albeit simplified) format for the data that
we’re likely to receive. To fetch it into our application, we can use jQuery’s
getJSON() method:
listing 2. javascripts/ch4/15-templating.js (excerpt)
$.getJSON("../data/spots.json", function(data){
// Got JSON, now template it!
});
|
With some data in our pocket, it’s time to show it to the world. The first of our three
approaches is to loop over the data array, building up HTML strings that
can then be added into the DOM:
listing 3. javascripts/ch4/15-templating.js (excerpt)
$.getJSON("../data/spots.js", function(data){
// Got JSON, now template it!
var html = "";
for(var i = 0; i < data.length; i++) {
html += "<li><a href='#'>";
html += "<h2>" + data[i].name + "</h2>";
html += "</a></li>";
}
$("#spots-list").append(html);
});
|
This is a very old-school approach, and while it can work for very simple examples, it
tends to scale poorly: it’s error-prone, difficult to maintain, and degenerates fairly
quickly into a huge mess of mashed-together HTML and data.
The separation of data and presentation is important, and this is especially
true for web applications. Using the previous method, if a designer wanted to make changes
to the HTML, they’d either have to know some JavaScript (and potentially risk breaking
something), or come and bother you about it. We would prefer to avoid being bothered, so a
better solution is to include the HTML where it belongs—in the HTML page:
listing 4. ch4/16-templating-2.html (excerpt)
<div id="tmpl-simple" style="display:none;">
<li>
<a href="spot.html" data-load="spot">
<h2></h2>
<span class="relative-distance"></span>
<span class="sightings"></span>
</a>
</li>
</div>
|
We’ve constructed a generic fragment that’s empty of any data. When we receive data from
the server, we can clone this template and fill it with our data. It can be as complex as it needs
to be, as long as there are some hooks for us to inject our data into. Of course, you don’t
want an empty row in the list shown to the user—so the template needs to be hidden. In the
above example, this is done using display: none;.
To make use of the fragment, we’ll clone it with jQuery’s
clone() method, and then inject all the pieces of data into their
correct elements using the text() method:
listing 5. javascripts/ch4/16-templating-2.js (excerpt)
$.getJSON("../data/spots.json", function(data){
// Got JSON, now template it!
$.each(data, function(){
var newItem = $("#tmpl-simple").clone();
// Now fill in the fields with the data
newItem.find("h2").text(this.name);
newItem.find(".relative-distance").text(this.distance);
newItem.find(".sightings").text(this.sightings);
// And add the new list item to the page
newItem.children().appendTo("#spots-list")
});
transition("#spots", "show");
});
|
This solution works well, leaving all the HTML in one file and all the JavaScript in
another. It’s much nicer than our first approach, and is suitable for small static
templates. But it’s less ideal if the HTML is continually changing, and for large pages it
requires us to write a lot of generic JavaScript to replace all the field values.
This is the kind of mind-numbing code that we want to automate away. Perhaps we
could add some tokens in the HTML that could be automatically replaced from a given data
object? That’s quite a good idea, and it turns out plenty of developers have had it before:
it’s called a templating engine. Choosing a templating engine is like
choosing a text editor: there are a lot out there, they have a many different features, most
of them are fine, and ultimately it’s up to you which one you (or your company) likes the
best.
To use the library, download the archive from GitHub and extract the minimized file. It
will be called jquery.tmpl.min.js, and is only about 8KB in size. Copy
this into your project to load it up:<script src="jquery.tmpl.min.js" type="text/javascript"></script>
1. Twitter Integration with Templating
Having brought out the big guns—the jQuery templating engine—we need something a bit
more juicy to use it on. The client has been toying with the idea of integrating some
Twitter data into the app. Specifically, they noticed that Twitter does a good job of
celebrity stalking too—and they want to harness that stalking power in their app.
The plan is to use a public search on the query term “celeb spotting,” and display any
tweets that match. Generally, you can’t make cross-domain Ajax requests (it’s a security issue—in fact, the same security issue that
prevents you from placing Ajax requests to file:// URLs)—but Twitter provides
data in JSONP format. JSONP is a common trick to grab data across domains by loading it inside a
<script> tag. For this to work, we need to append the
string "callback=?" to the URL request.
We’ve had a look at the Twitter API
documentation, and found the appropriate URL to conduct a search:
http://search.twitter.com/search.format?q=query.
All we need to do is call getJSON() with that URL and our search string:
listing 6. javascripts/ch4/17-twitter-templating.js (excerpt)
var twitQuery = "celeb+spotting",
twitUrl = "http://search.twitter.com/search.json?q=";
$.getJSON(twitUrl + twitQuery + "&callback=?", function(data){
// Show some tweets
});
|
Now we have some data from Twitter! The tweets are stored in the results property of the data object as an array, so we
can use them directly in our template system.
Templates used in templating engines are defined in the same manner as the
simple template we put together earlier: as elements inside our HTML file. The jQuery
templating engine uses a clever trick to prevent the template from becoming a regular part
of the DOM: it hides inside a <script> tag with type="text/x-jquery-tmpl". Browsers don’t know how to handle this type of
“script,” so they don’t try to execute it, but we can still extract the content and use it
as a template:
listing 7. ch4/17-twitter-templating.html (excerpt)
<script id="tmpl-tweet" type="text/x-jquery-tmpl">
<li>
<a class="avatar" href="#"><img src="${profile_image_url}"
=>alt="${from_user}"></a>
<a href="http://twitter.com/${from_user}"><h2>${from_user}</h2></a>
<span class="details">
${text}
</span>
</li>
</script>
|
Tip:
Template Types
Even if you’re not using the jQuery templating engine, you can use the <script> trick with your own templates. You should use a
different option to <x-jquery-tmpl> though; you can
make up your own <type> if you want, but you’ll often
see the <text/html> used.
You’ll notice that inside the HTML there are some odd-looking bits. These are are our
tokens that will be replaced with data. The strings inside the curly braces aren’t made
up: they are the names of the values that are returned to us from Twitter. To find out
exactly what data is returned by a given API call, you can either read the documentation,
or simply log the data object to the console using console.log(data);.
All that’s left to do now is to call the templating function. Just select the template
item by <id> (we’ve called ours <tmpl-tweet>), and call the tmpl()
method, passing the data.results array we retrieved via Ajax. The
engine will see that our array has multiple items; for each item it will generate a clone
of the template and replace all the tokens with the corresponding values from the data
array. The result is a jQuery object containing the constructed DOM node, which we can
inject into the page as we’d normally do:
listing 8. javascripts/ch4/17-twitter-templating.js (excerpt)
$.getJSON(twitUrl + twitQuery + "&callback=?", function(data){
$("#tmpl-tweet")
.tmpl(data.results)
.appendTo("#spots-list");
transition("#spots", "show");
});
|
No need to do any looping or manual value insertion. The combination of data-rich APIs
and simple templating engines gives you a lot of power as a developer, easily enabling you
to enrich your applications with information from a variety of sources. A sample result
from our Twitter search is shown in Figure 1.
The jQuery template engine also includes tags for conditional evaluation like {{if}} and {{else}}, so you can show different parts of
your template according to the situation. Check out the documentation for the full breakdown of
available functionality.