Creating a dynamic jQuery Mobile list view with ColdFusion, Mustache templates and JSON data

Filed under: ColdFusion, JavaScript, jQuery Mobile

comments (8) Views: 8,037

If you're like me and build web apps all day then the notion of templates and how to use them has likely reared it's head before. Best practices tell you to keep your markup and your code separate but how is that possible when your code mixes server side and client stuff together? In some cases the same exact HTML needs to be output from both sides. How do you accomplish that without duplicating efforts? Luckily we have Mustache templates (and others) to save us from ourselves. In this blog post I'm going to show you how you can leverage a single Mustache template for use in both ColdFusion and JavaScript.

Download the code

Let's start with a demo shall we (I started my project with the jQuery Mobile Boilerplate project). This example pulls the 20 most recent items from my Twitter feed in JSON format (hey, this is MY blog post after all). There's a toggle which allows you to switch back and forth between a list view generated by ColdFusion, and another that's pulled directly from Twitter via jQuery using the $.getJSON method. None of that is all that impressive, it's code that each of us probably written dozens of times, or more. What's really cool is that both ColdFusion and jQuery are using the same Mustache template.

What exactly IS Mustache? Their description, "Logic-less templates", leaves a little something to be desired. Essentially it's a way to abstract your HTML into a string of text which can be iterated over for a data set. Each item in the data set uses the same template, but replaces the contents of the template with the proper variables. The great thing is that Mustache is just a spec which can, and is, implemented in any language. ColdFusion has Mustache.cfc and JavaScript has Mustache.js. Let's look at the template this demo uses.

Mustache can be much more intricate than this simple example, but I wager that for most developers this is about all that we need. In the example above, every string contained within double braces (or mustaches), is a variable name that will be replaced for each pass in a loop. Mustache expects an object containing variables with these names. In ColdFusion this is a struct, in JavaScript it's an object.

The ColdFusion code

There's two main parts to the ColdFusion code. Let's look at it first, then dissect what it's doing.

In the first part we set up Mustache.cfc, the template string, pull down the Twitter data, and deserialize it. The second part is a simple loop, with the only difference is that we're not mixing HMTL code into our loop. Mustache takes care of that for us. Pretty simple isn't it? The JavaScript code is almost as simple. Let's check that out now.

The JavaScript code

Because this demo uses jQuery Mobile we set our jQuery code inside a live binding for pageinit, rather than the typical $(document).ready() block. The first thing this code does is call out to Twitter.com to get the feed. We're using JSONP (or JSON with padding) to get around JavaScipt's cross domain policy. This lets us call Twitter without requiring a server side proxy. Inside the success callback for the feed request, we make another AJAX call to load in the template. When that template data comes back we simply loop over it using jQuery's $.each() method. The guts of that loop look JUST like the ColdFusion code. We create an intermediate object which gets passed into Mustache. The only difference is that instead of directly outputting, or appending, the string of HTML we're concatenating it into a long string of HTML. Lastly, we append the string of HTML to the UL one time, then call the refresh method of the jQuery Mobile listview.

Pro-tip: Concatening HTML in this manner prevents jQuery from making an insert call to the UL tag for every loop, a costly practice.

So there you have it. While this demo uses only a single template you can easily see how valuable this approach would be in a large application. So get out there and start using templates!

Amazon logo

If this article was interesting, or helpful, or even wrong, please consider leaving a comment, or buying something from my wishlist. It's appreciated!

For those of you that might have tried the demo, try again please. Looks like my server was blocking files with the .mustache extension. I've changed the template to a plain text file and it works now.

andy matthews - March 02, 2012 12:42 pm

Thanks for this Andy, really helpful.
I am trying to integrate this with my code. Have it working for a simple list, but when I try a nested list, it seems to go into an infinite loop, eating memory etc.
Something to do with the refresh. Any ideas?

Gareth Kelly - May 17, 2012 10:38 am

Gareth...I assume you're talking about ColdFusion? Do you happen to be at cfObjective? I'm here, and if you're here then you could perhaps show me your code. If not, could you provide a sample for me to review?

andy matthews - May 17, 2012 10:07 pm

Whoops, that didnt format very well.

Code again
<pre>
<script>
$('div[data-role=page]').live('pageinit', function(event){
$.getJSON("http://localhost/myfile.json", function(data){
var s="";
$.each(data.zone.company, function(i,item){
var t="";
//t = "<li><h3>" + item.name.paratext + "</h3><ul><li>" + item.currency.paratext + "</li></ul></li>";
t = "<li><h3>" + item.name.paratext + "</h3></li>";
s = s + t;
});

$('#main-ul').append(s).listview('refresh');

});

});
</script>
</pre>

and
<pre>
<div data-role="content" data-theme="h">


<ul id="main-ul" data-role="listview" data-theme="c" data-divider-theme="i" data-inset="true">
<li data-role="list-divider">List</li>

</ul>


</div>
</pre>

Gareth Kelly - May 18, 2012 05:48 am

My comment system isn't the greatest. It "executes" HTML. But that's what you get when you write it yourself.

Do you have an online demo I can look at?

andy matthews - May 18, 2012 10:07 am

The client side example does not show any Tweets.

Also, if I'm getting back data from a Coldfusion search query as a "ready-made" structure, there is no more need to re-declare data, I'll just try to feed in my object.response.data?

Frequent - May 22, 2012 08:45 am

Frequent...

The client side demo isn't showing tweets because I wasn't properly initializing the AJAX call with jQuery Mobile page events. I've changed it now.

andy matthews - May 22, 2012 08:04 pm

Also the problem with getting back results directly from ColdFusion is that it's built in JSON format is...well...odd. It's not consistent with normal array based JSON formatting so it requires manipulation either on the client side or server side before it can be used.

andy matthews - May 22, 2012 08:05 pm