Quick example of jQuery Templates

This post is more than 2 years old.

Earlier today Rey Bango posted an excellent article about jQuery Templates (Not Using jQuery JavaScript Templates? You're Really Missing Out.) This was something I had meant to look at before but just never got around to it. If you haven't looked at this feature, please stop reading and catch up on Rey's post. After reading it, I thought it would be cool to employ the technique to update the demo I posted earlier today (Another simple jQuery/ColdFusion example). It took me a grand total of five minutes. Here is the original code used to render categories:

$.getJSON("data.cfc?method=getcategories&returnformat=json&queryformat=column", {}, function(res,code) { //create a string for our result var s = "" for(var i=0; i<res.DATA.ID.length; i++) { s += "<a href='' class='navLink' id='nav_" + res.DATA.ID[i]+ "'>"+res.DATA.NAME[i]+"</a><br/>" }

//"draw" s onto the screen
$("#nav").html(s)

})

Compare that with the template version:

<script id="categoryTemplate" type="text/html"> <a href="" class="navLink" id="nav_${ID}">${NAME}</a><br/> </script> (more stuff here cut out...) //Call the CFC to get queries $.getJSON("data.cfc?method=getcategories&returnformat=json&queryformat=column", {}, function(res,code) { var newData = [] for(var i=0; i<res.DATA.ID.length; i++) { newData[newData.length] = { "ID":res.DATA.ID[i], "NAME":res.DATA.NAME[i]} } $("#categoryTemplate").tmpl(newData).appendTo("#nav") })

As you see - I had to reform the data returned by ColdFusion to make it work with the template engine. I could do this at the CFC, but I like my CFC being abstract and not tied to any implementation. So I didn't trim many lines of code here (I may have actually went up by one or two), but the way it works is much cleaner now. I'm reminded of Adobe Spry, which to me has always shined in the area of actually displaying Ajax content.

Next up I rewrote the detail portion:

<script id="detailTemplate" type="text/html"> <h2>${NAME}</h2> This person is ${AGE} years old. </script> (more stuff here....)

//listen for clicks on navLink $(".navLink").live("click", function(e) { var clickedId = $(this).attr("id") var id = clickedId.split("_")[1]

//load the detail
$.getJSON("data.cfc?method=getdetail&returnformat=json", {"id":id}, function(res,code) {
	$("#content").html($("#detailTemplate").tmpl(res))
})
	
e.preventDefault()

})

This modification was even simpler. My simple CFML struct worked just fine for the template engine. All in all a very painless modification, but I really dig it. You can find out more about the Template plugin here: http://github.com/nje/jquery-tmpl

Here is the entire page for the new version:

<html>

<head> <script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1/jquery.min.js"></script> <script type="text/javascript" src="jquery.tmpl.js"></script>

<script id="categoryTemplate" type="text/html"> <a href="" class="navLink" id="nav_${ID}">${NAME}</a><br/> </script>

<script id="detailTemplate" type="text/html"> <h2>${NAME}</h2> This person is ${AGE} years old. </script>

<script> $(document).ready(function() {

//Call the CFC to get queries
$.getJSON("data.cfc?method=getcategories&returnformat=json&queryformat=column", {}, function(res,code) {
	var newData = []
	for(var i=0; i&lt;res.DATA.ID.length; i++) {
		newData[newData.length] = { "ID":res.DATA.ID[i], "NAME":res.DATA.NAME[i]}
	}
	$("#categoryTemplate").tmpl(newData).appendTo("#nav")
})

//listen for clicks on navLink
$(".navLink").live("click", function(e) {
	var clickedId = $(this).attr("id")
	var id = clickedId.split("_")[1]
	
	//load the detail
	$.getJSON("data.cfc?method=getdetail&returnformat=json", {"id":id}, function(res,code) {
		$("#content").html($("#detailTemplate").tmpl(res))
	})
	
	e.preventDefault()
})

}) </script> </head>

<body>

<div id="nav"></div>

<div id="content"></div>

</body> </html>

Raymond Camden's Picture

About Raymond Camden

Raymond is a senior developer evangelist for Adobe. He focuses on document services, JavaScript, and enterprise cat demos. If you like this article, please consider visiting my Amazon Wishlist or donating via PayPal to show your support. You can even buy me a coffee!

Lafayette, LA https://www.raymondcamden.com

Archived Comments

Comment 1 by Brian posted on 7/9/2010 at 11:17 PM

Took me a bit (new to JQuery), but I was getting an error: "res.DATA.ID is undefined". Finally realized that I was dealing with case sensitivity -- your QueryNew used lcase id and name. Is this common for any data retrieved? Will it always be case sensitive?

Comment 2 by Raymond Camden posted on 7/9/2010 at 11:20 PM

JS is always case sensitive. Will it always be upper case? No. When you make a struct in CF using this notation: person.name = "RAY", then when it gets serialized to json, it becomes upper cased. If you do: person["naMe"] = "RAY", then the case of that key is preserved.

Comment 3 by salvatore fusto posted on 7/10/2010 at 11:22 AM

Hy Ray,
i noted that you converted the result from the cfc in an array of hashes/structs, why not obtain a json array of hashes directly?
regards

Comment 4 by Raymond Camden posted on 7/10/2010 at 5:51 PM

Are you asking why I didn't return the data in a way that would work automatically? I certainly could have. However I try to keep my CFCs pure. Right not it returns a query. I may have other CFMs files that need this data and they would work with the query just fine. Just because one client (my code here) needs it in another way, it doesn't mean I should change the CFC.

That's my opinion anyway. Certainly if you are 100% sure the only "user" of this data is the Ajax front end, then yeah, go ahead.

Comment 5 by Jim O&aposKeefe posted on 7/10/2010 at 10:08 PM

Ray, I'm lost at the "Why are we doing this?" level. It seems like dropping data into templates is what coldfusion does best, and yet your pushing that off onto the client and jQuery. Coldfusion becomes a facade for the database. Maybe the simplicity of the demo page obscures the helpfulness of this approach. Am I making any sense that you can comment on?

Comment 6 by Raymond Camden posted on 7/11/2010 at 2:30 AM

There are a few reasons why I prefer this way. If you look at the first example, to render the content I had to build JS strings. While doable, it gets messy. There are a lot of plus signs, I have to escape the quotes or use single quotes. The template method is much easier to read and update.

As to your point about why I didn't use CF - or why I just use CF as a gateway to the db. I don't see anything wrong with that. CF is a great 'glue' between servers and front end clients. Just because it didn't do a lot here doesn't mean it wasn't useful. It also speaks to the power of the Ajax plumbing built within CF - specifically - I write my business logic with NO client in mind. But to expose it to an Ajax client I just make use of returnForm. And I'm done. To me that just shows CF shines.

Comment 7 by salvatore fusto posted on 7/12/2010 at 5:14 PM

Yes, you are absolutely right saying that the main goal is abstraction; however you can also structure your cfc to return data formatted as requested from front end: using a simple key in the url you can get data as query, object collection, pure json-ed query or json array of struct.
regards

Comment 8 by Raymond Camden posted on 7/12/2010 at 5:19 PM

Your talking about the structure of the data. You aren't talking about formatting in the display sense. That's what this template engine is for. Ie, take the data, wrap the name in h2, etc. I think it's a secondary discussion.

So if we ignore _html_ display, let's focus on your idea of making the CFC return different forms of data. IMO, this is a bad idea. The CFC is "right". It is asked to return a query of data and it does. Ajax can use it. CF can use it. It just works. In our example here we have a jQuery plugin which is a bit anal about the form of data. Therefore, I manipulate it before feeding it to the plugin. Could the CFC have done that. Sure! But why clutter it up when it isn't a CFC concern?

Now that being said, there have been cases where I've built other CFCs to do stuff like this. A good example is a service to handle calls for jqGrid. jqGrid also has specific needs for the form of the data. So sometimes I've built a CFC that both a) exposes another CFC remotely and b) handles changing the data. But I do NOT mess with the core CFC. It's business logic remains pure and is not concerned with any one client.

Comment 9 by cjm posted on 7/16/2010 at 7:36 PM

Great post Ray! Unfortunately I found this a single day late after I just did some extensive javascript outputting the now "long" way. Looks like I have some re-work to do! :)

Comment 10 by Shekhar posted on 9/10/2010 at 12:56 PM

Will try this definitely..quite good.