New High Performance jQuery Templating Engine

Damyan Petev / Monday, April 9, 2012

As an avid reader of our blogs (we are sure you are), you might have noticed some topics regarding templating our controls or parts of them. As part of that came a minor catch – templating required (and as long as we are waiting for 12.1, it still does) the jQuery Templates Plugin and as a part of roadmap change it was decided that it will not be developed past its current beta status and an entirely new one is in planning. We strive to provide a well-rounded package of functionality with our jQuery product, so you wouldn’t usually need to look for additional solutions to support out controls. Along with that, we make sure to pack plenty of all the advantages new technologies provide so you get a product that goes..fast! I pointed those two ideology pillars of the product, because both of them led to the creation of our very own Template Engine – we made sure our users can use such features, while being certain we would provide support and most importantly we will strive to make the most out of it – we offer controls supporting very large amounts of data and now we offer the engine to template them swiftly.

Introduction

Let’s provide some basics for those unfamiliar with templating. Such technique provides a way to define a one layout(to be applied to many records) and indicate how and where each piece of your data should be via a special syntax that would be recognized by the template-providing scripts. You can then relax while the latter take your data, find it’s place, replace it and build your layout. It is in essence creating a tailored-to-your-data layout on the client side – that can reduce the load on your server! Now you have that much greater control over where should UI generation put its weight. Also it needs to support the current functionality covered by jQuery Templates and for that reason differences in terms of how it functions are kept to the bare minimum as far as the developer using the controls is concerned – if you are already using templating for our grid we would not ask you to learn something completely new or you might not even need to change you code.

And that is, of course, not not the full story yet – while it was needed for some controls, the template engine was designed to be fully functional stand-alone feature. Which means that you can use the engine to template any data anywhere into anything really. All you really need is jQuery defined and to load igTemplating which can be done like so:

  1. <script type="text/javascript">
  2.     $.ig.loader({
  3.         scriptPath: "../../Scripts/Infragistics/js/",
  4.         cssPath: "../../Content/Infragistics/css/",
  5.         resources: "igTemplating"
  6.     });
  7. </script>

At this point you have a tmpl method available in the infragistics namespace – $.ig that you can call to ‘render’ you template.

The engine is fully integrated in out tree, combo and grid controls, which means you don’t even have to reference it(as seen above) – it’s enough to load one of the above and templating will be made available as well.

So to make it short – you get a high-performance templating engine capable of working stand-alone as well as integrated with our controls and in a format you might be already familiar with or at least is easy to learn.

In a snap!

So we are to talk performance – if and until we do publish results from some official testing, you might have to trust my word for it, but as we said before it was designed with that main goal in mind. That led to separating the template producing logic into two branches – one that would handle the simple property –>actual data substitution and it is lightning fast. The second would handle more complex operations including, such as conditions and loops (will explain those below) and those operations require creating a JavaScript function to apply those ‘rules’ and it must be executed on every pass. That means it would be somewhat slower. One Other thing that can affect performance is encoding and you are, of course, given a way to control it.

Furthermore, the engine outputs a sting rather than objects (which makes for great performance) and some preliminary testing attempts showed mind-boggling speeds when you use it for simple substitution without encoding and even if encoding is required it will outperform one of available alternatives (that itself is much faster the the jQuery Templates)!

What can you do with it?

The engine will recognize a certain grammar, if you will. Along with all your data properties (tokens) in the template, the engine has denied regular expressions to help it identify comments(enclosed with ‘#’) and substitutions(data tokens) in the following formats:

  • ${prop} – encoded
  • {{html prop}} – with encoding

The difference with encoding , besides performance, is that if your data contains HTML itself the output will be different. Let’s say you have data like:

  1. { Name: "<img/>ASP.NET", Color: "olivedrab2", Color2: "#B3EE3A/#9ACD32" },
  2. { Name: "<a href=\"#\">Windows Forms<a>", ...

And depending on whether you use {{html Name}} or ${Name} in you template you will get accordingly:

Data property that has not been encoded by the Templating Engine

or when encoded:

Data property that has been encoded by the Templating Engine

A very basic substitution

Assume you have a HTML ul element with id ‘productListing’ and the following data and script:

  1. //------ Basic:------
  2. var products = [
  3. {Name: "Chai", InStock: "33", OnOrder: "2" },
  4. { Name: "Chang", InStock: "17", OnOrder: "40" },
  5. { Name: "Aniseed Syrup", InStock: "13", OnOrder: "70" },
  6. { Name: "Chef Anton's Cajun Seasoning", InStock: "53", OnOrder: "0" },
  7. { Name: "Chef Anton's Gumbo Mix", InStock: "0", OnOrder: "0" },
  8. { Name: "Grandma's Boysenberry Spread", InStock: "120", OnOrder: "0" },
  9. { Name: "Northwoods Cranberry Sauce", InStock: "6", OnOrder: "20" },
  10. { Name: "Mishi Kobe Niku", InStock: "52", OnOrder: "20" },
  11. { Name: "Uncle Bob's Organic Dried Pears", InStock: "15", OnOrder: "33" }
  12. ];
  13. /* THE template */
  14. var theTemplate = "<li><b>${Name}</b> ({{html InStock}}) [Ordered: {{html OnOrder}}]</li>";
  15. /* Render the template */
  16. var result = $.ig.tmpl(theTemplate, products);
  17. /* Add the ready markup to the div: */
  18. $(result).appendTo("#productListing");

The result from this would be something like this (depending on page default styles):

A list populated with Templating Engine using simple data substitution

As you can see you can build html markup, mark places for data and it will be generated for each of your records. Now things get a bit more interesting if you have some hierarchical data:

  1. //------ With nested:------
  2. var productsWithHierarchy = [
  3. { Name: "Chai", InStock: "33", OnOrder: "2", Test: { Inner1: "inner text 1.1", Inner2: "inner text 1.2"} },
  4. { Name: "Chang", InStock: "17", OnOrder: "40", Test: { Inner1: "inner text 2.1", Inner2: "inner text 2.2"} },
  5.  { Name: "Aniseed Syrup", InStock: "13", OnOrder: "70", Test: { Inner1: "inner text 3.1", Inner2: "inner text 3.2"} },
  6. ];
  7. /* THE template */
  8. var theTemplate2 = "<li><b>${Name}</b> ({{html InStock}}) [Ordered: {{html OnOrder}}] <ul><li> {{html Test.Inner1}}</li> <li>{{html Test.Inner2}}</li></ul> </li>";
  9. /* Render the template */
  10. var result2 = $.ig.tmpl(theTemplate2, productsWithHierarchy);
  11. /* Add the ready markup to the div: */
  12. $(result2).appendTo("#hierListing");

Notice the ‘Test.Inner1’ - navigating nested properties is very easy to do and the result from this is:

A nested list demonstating the Templating Engine's capability to use hierarchy

What if…

So, what happens when you don’t want to apply the template blindly to all records? - You build a conditional block to handle that. The engine recognizes a number of directives such as logical blocks:

  • {{ if }} conditionals
  • {{else}} conditionals
  • {{elseif}} conditionals
  • {{each}} loop

And a reserved $i identifier for the current index in a loop. All that can make up for some pretty interesting capabilities.

You can go very deep with this as you can nest multiple else-if conditions inside the ‘if’ and the ‘else’ will be your default. All blocks must be closed to be recognized and their closing tag is the same with added slash. However, there is something very important to note – due to the way blocks are recognized, it is not possible to have two consecutive ‘if’ blocks, as in:

{{if condition}} /* work 1 */ {{/if}} {{if condition2 }} /* work 2 */ {{/if}} – as the very last closing will be assigned to the very first and therefore will not allow for such logic. At this point though let’s have a relatively simple example – same as the first simple substitution lets have our products, this time however lets check if we have enough items in stock to cover our orders and provide some sort of visual for insufficient ones – yes we can do all that in the template like so:

  1. //------ Conditional:------
  2.  var theTemplate3 = "#This time we will be using table as output#" +
  3.  "<tr> <td><b>${Name}</b></td>" +
  4.  "# Here is the IF block #" +
  5.  "{{if (${InStock} - ${OnOrder}) < 0}} <td style=\'color:red;\'>(${InStock})</td>' +
  6.  "# And the Else (default) block with the same template but without the style #" +
  7.  "{{else}}<td>(${InStock})</td> {{/if}}<td>[Ordered: ${OnOrder}]</td> </tr>";

Resulting in:

A table demonstating the use of conditional blocks with the Templating Engine

So we made a very simple warning system for our user. You can apply the same type of logic to check the record index (using the reserved ‘$i’) to apply special templates to select rows and naturally create alternative row layouts and so on.

Of course, you are not limited to just local arrays – the Templating engine can take data from functions and it can pass parameters if required. Along with that, you have all the freedom to produce markup of any kind and that includes placing data inside HTML elements classes, id-s.. you name it! Here’s an example, where a few styles applied to class names and the said class names are available in our data:

  1. //------Data function------
  2. function productFamily(count) {
  3.     var NetAdvantage = [
  4.     { Name: "ASP.NET", Color: "olivedrab2", Color2: "#B3EE3A/#9ACD32" },
  5.     { Name: "Windows Forms", Color: "turquoise2", Color2: "#00E5EE" },
  6.     { Name: "WPF", Color: "slateblue4", Color2: "#473C8B" },
  7.     { Name: "Silverlight", Color: "magenta4", Color2: "#8B008B" },
  8.     { Name: "jQuery", Color: "mediumaquamarine", Color2: "#66CDAA" },
  9.     { Name: "SharePoint", Color: "gold", Color2: "gold" },
  10.     { Name: "iOS", Color: "gray", Color2: "gray" },
  11.     { Name: "Silverlight Data Visualization", Color: "orange", Color2: "orange" },
  12.     { Name: "WPF Data Visualization", Color: "yellow", Color2: "yellow" },
  13.     { Name: "Windows Phone", Color: "dodgerblue", Color2: "#1E90FF" },
  14.     { Name: "Visual Studio LightSwitch", Color: "darkslateblue", Color2: "#483D8B" },
  15.     { Name: "TestAdvantage", Color: "red", Color2: "red" },
  16.     { Name: "Icons", Color: "green", Color2: "green" }            
  17.     ];
  18.     var result = [];
  19.     if (count > NetAdvantage.count()) { count = NetAdvantage.count(); }
  20.     for (i = 0; i < count; i++) {
  21.         result.push(NetAdvantage[i]);
  22.     }
  23.     return result;
  24. }
  25. var theTemplate4 = "<div class=\"${Color}\">{{html Name}}</div>";
  26. /* Render the template */
  27. var result4 = $.ig.tmpl(theTemplate4, productFamily, [7]);

This template will simply cause DIV-s for each product to be generated with class that contains their color (by which the CSS will be applied). Also calling the data function we provided parameter - the number of products to display. The result looks like:

Template created using data from a function and applied style using the Templating Engine to set class for each

Beyond

The igTemplating Engine truly gives you a lot of freedom - in terms of how to place your content, whether you would use it on it’s own or you can do some eye-catching wizardry with the tree, combo or grid and enhance them with functionality and style at the same time. Since you can easily build markup, you can take data that was not readily available and essentially format in a way that a UI control can transform – yes, that means templating table to turn then in an igGrid form example, or a table in another table and.. well you get the point. You can assign classes and Ids and you can target those to turn them into completely different UI. You can also enjoy the functionality that was so far available to template our jQuery tree and grid controls and soon you will also have a column template for the grids, in addition to the current row option.

The good news around customizing your data presentation are far from over and this new Templating Engine is ready to take care of it and most importantly do it extremely fast!

Expect a demo for this blog once 12.1 is made available, samples and more ways you can make the best out of such functionality. Meanwhile you can check out blogs that demonstrate templating (with jQuery Templates, though they should still give you an idea of the capabilities):

And as usual you can check out the samples for each of those controls for more.