jQuery Templating and Data Linking Coming to a Site Near You
Finally!!! I was so excited to finally see the jQuery Team and Microsoft finally release the Templating and Data Linking plugins last week. If you have attended one of my WCF, jQuery sessions this year, or have been following my jQuery posts, you saw how I was using the micro templates function to bind JSON data to HTML templates on the client. After the MIX announcement of Microsoft developing the Templating plugin I adjusted that demo to use the new technique and have even given some dedicated sessions about the Templating and Data Linking plugins over the course of the summer. Now that the two plugins are official and I am going to share my simple templating demo application.
Templating
"If you caught one of my talks you know I created this little demo while watching the Deadliest Catch and used it as a silly theme, The Crabiest Catch. Silly, I know, but it gave me the opportunity to create a Tree list using nested templates. The sample was built using WebMatrix and the new Razor syntax, but that is not ultimately the focus for the demo. Just wanted you to be aware of that if you tried to open the site in Visual Studio, you will need to make some adjustments.
The server-side components for this demo are the page, which hurls the initial markup to the browser and a second page that supplies the JSON formatted data. For this demo these two pages are index.cshtml and crabby.data.cshtml.
First the index.cshtml page. The page contains the initial page markup and the templates that will be used to generate the actual page content. The page uses a Layout or the WebMatrix equivalent of a Master Page to render the standard layout.
<h1>Who's the Crabiest?</h1>
<ul id="crabBoats" class="tree"></ul>
That’s right that is the entirety of the initially rendered page content a single H1 and UL tag without any LI elements. I call this the initial canvas, which will eventually be confusing when HTML5 becomes common. The UL will eventually contain the tree node, hence the class ‘tree’. The actual code and pattern for the tree is lifted from the jQuery Cookbook, which I highly recommend as jQuery reference book.
The server-side classes I use in this example are located in the App_Code folder. The Ship object is a C# class:
public class Ship
{
public Ship()
{
CrabStrings = new List<CrabString>();
}
public string ShipName { get; set; }
public string Captain { get; set; }
public List<CrabString> CrabStrings { get; set; }
}
The Ship object graph gets serialized in the crabby.data.cshtml file using the Response.Write method using the Json.Encode method. This is a change from the first Beta where you could use JsonWrite to perform this action.
@{
Response.Write(Json.Encode(FakeHelper.GetShips()));
}
This serialized the Ship object graph to a JSON string and returns it to the client with the proper headers, etc.
In the Index.cshtml there is a script block containing a typical jQuery $.ajax call to retrieve the data. The success callback is an anonymous function that parses the JSON into a JavaScript object. It then uses the templating plugin’s tmpl() function to build the tree. It appends the resulting HTML to the #crabBoats UL shown above.
var ships = {};
$(document).ready(function() {
$.ajax({
url: 'crabby.data.cshtml',
type: "GET",
processData: false,
contentType: "application/json",
timeout: 1500,
dataType: "text", // not "json" we'll parse
success:
function(res) {
// *** Use json library so we can fix up MS AJAX dates
var result = JSON.parse(res);
if (result.ExceptionDetail) {
alert(result.Message);
return;
}
ships = result;
$( "#shipRow" )
.tmpl( ships )
.appendTo( "#crabBoats" );
$('#crabBoats li:nth-child(even)').addClass('evenRow');
init_tree();
},
error: function(){
alert('Error, does not compute');
}
});
});
The template I use here is called ‘shipRow’. The template defines how a top level LI element should render. It is a script block with the id ‘shipRow’. As I explain in my AJAX talks the use of the script tag here means the markup will not be rendered to the user. Also setting the type to ‘text/html’ keeps it from blocking the rendering engine like a normal script element does, meaning it does not adversely affect performance.
<script id="shipRow" type="text/html">
<li><a href="" class="tree_trigger"> </a><span class="shipNode">${ShipName + ' - Captain: ' + Captain}</span><br/>
<span class="totalPots">Total Pots: ${getTotalPots($data.CrabStrings)}</span>
{{tmpl($data) "#stringTemplate"}}
</li>
</script>
The template uses several techniques for merging data into the markup. Merge locations are designated by the ${} or {{}} syntax. First the ship node is composed of two fields, ShipName and Captain. The merge syntax here is ${ShipName + ‘ – Captain: ‘ + Captain}. Both ShipName and Captain are properties of the ship object. So this technique shows you can actually use more than one field to compose a merge field.
The next technique uses a function to calculate the ship’s number of pots. {$.getTotalPots($data.CrabStrings)}. Here a function is used to generate a value that is merged into the markup. I used the $data.CrabStrings here just to show you can explicitly use the $data object, it simply refers to the object passed to the $.tmpl() function. It is just inferred as used in the first field. CrabStrings is an array property in the Ship object. It contains a nested value of NumberOfPots, which is used to calculate the total number of pots in used by each crab boat.
function getTotalPots(crabStrings){
var totalPots = 0;
for (var i = 0, l = crabStrings.length; i < l; i++) {
totalPots += crabStrings[i].NumberOfPots;
}
return totalPots;
}
The last merge field calls the $.tmpl() function to call a nested template. This is a very powerful feature because it gives you the freedom to define independent templates and as we will see in a future blog post conditionally call to create some very advances layouts. For this demo the #stringTemplate defines the child list of the ship’s crab pot strings.
<script id="stringTemplate" type="text/html">
<ul>{{each CrabStrings}}<li><a href="" class="tree_trigger"> </a><label class="crabString">
${$value.StringName} - Pots: ${$value.NumberOfPots}</label>
<ul >
<li>Latitude: ${$value.Latitude}</li>
<li>Longitude: ${$value.Longitude}</li>
</ul>
{{tmpl($value) "#potTemplate"}}
</li>{{/each}}</ul>
</script>
This template introduces another concept, the {{each}} Template Tag. This tag effectively performs a for each over the array of CrabStrings. So this template creates a single UL element then proceeds to generate a LI element node for each crab pot string. I also use $value to access the CrabString object within the each loop.
The $value object is simply a reference to the object that in use in the loop. Here $value is a single CrabString object because CrabStrings is an array of CrabString. You can also access the $index property, which refers to the current index in the iteration. Because a CrabString is an object with properties, you use . notation to access the properties, like ‘StringName’.
Before the CrabString’s LI is closed another nested template, #potTemplate is called. This creates a nested list of crab pots that belong to the string. It operates similarly to the #stringTemplate.
<script id="potTemplate" type="text/html">
<ul>{{each $data.Pots}}<li><label>${$value.PotNumber} - Crabs: ${$value.Crabs}</label></li>{{/each}}</ul>
</script>
As you can see the Templating plugin provides some very powerful functionality to build dynamic user experiences using AJAX techniques. This demo just touches the surface of what you can accomplish with the plugin. I will continue to expand this sample application over the coming months and combine functionality with the Data Linking plugiin to demonstrate how to build MVVM AJAX applications.
The full Templating plugin’s documentation is available on the jQuery site. I encourage you to go read it. You can download this sample jQuery Templates WebMatrix based web site from my Blog. I have several things I want to explore with the jQuery Templates, Data Linking and advanced uses of Webmatrix in the next month, so keep an eye out for those post.