April 2012
Volume 27 Number 04
Client Insight - Using JsRender with JavaScript and HTML
By John Papa | April 2012
Many development platforms use templates to reduce code and simplify maintenance, and HTML5 and JavaScript are no exception. JsRender is a JavaScript library that allows you to define a boilerplate structure once and reuse it to generate HTML dynamically. JsRender brings a new templating library to HTML5 development that has a codeless tag syntax and high performance, has no dependency on jQuery nor on the Document Object Model (DOM), supports creating custom functions and uses pure string-based rendering. This column discusses scenarios for which JsRender is ideal and demonstrates how to use its various features. All code samples can be downloaded from msdn.com/magazine/msdnmag0412, and JsRender can be downloaded from bit.ly/ywSoNu.
According to Boris Moore and the jQuery UI team blog, in April 2011 the team decided to put jQuery templates on hold in favor of creating a logic-less and string-based rendering library. This led Moore (one of the driving forces behind jQuery templates) to create JsRender and JsViews. These two libraries are effectively the replacements for jQuery templates. You can read more on the history of templating and how JsRender has evolved on Moore’s post about the jQuery Templates and JsRender roadmap (bit.ly/AdKeDk) and the jQuery team’s post (bit.ly/fhnk8A). Client Insight will explore JsRender and JsViews over the next few months.
Why Templates?
Using templates with JavaScript reduces and simplifies code. Without templates, adding a series of list items and other HTML elements for a set of data might require manipulating a browser’s DOM. This is where templating using a plug-in such as JsRender can be quite useful to do the heavy lifting. For example, let’s assume you retrieve a set of movies and you want to display them. You could write JavaScript and manipulate the DOM, but the following code shows that even this simple task can become difficult to read, even with the help of jQuery:
// Without a template
var i = 1;
$(my.vm.movies).each(function () {
var movie = this;
$("#movieContainer1").append(
"<div>" + i++ + ": " + movie.name + " ("
+ movie.releaseYear + ")</div>");
});
The code is entirely written with JavaScript and jQuery, but it can be difficult to discern the HTML from the data from the JavaScript. Using a template, we can more easily separate the structure and eliminate most of the JavaScript code. The following template (01-with-and-without-templates-with-jquery.html in the accompanying code download) demonstrates this concept:
<script id="myMovieTemplate" type="text/x-jsrender ">
<div>{{:#index+1}}: {{:name}} ({{:releaseYear}})</div>
</script>
Notice that the template is wrapped in a script tag, its type is set accordingly and it’s given an id so it can be identified later. Rendering a template requires three aspects: a template, data and a container. The template defines how the data will be rendered and the container defines where to render it. The following code shows how to render a list of movies using the template named myMovieTemplate to an element with the id of movieContainer:
$("#movieContainer").html($("#myMovieTemplate").render(my.vm.movies));
This example uses jQuery to simplify the syntax. It’s important to note that JsRender isn’t dependent on jQuery. The code to use JsRender to render the data using the template could also be written as shown here (02-jsrender-no-jquery.html in the code download):
my.vm = {
movies: my.getMovies()
};
jsviews.templates({
movieTemplate: document.getElementById("myMovieTemplate").innerHTML
});
document.getElementById("movieContainerNojQuery").innerHTML
= jsviews.render.movieTemplate(my.vm.movies);
Rendering Templates
You can render templates using JavaScript in several ways. First you’ll want to define your template either as a string or in a <script> tag. The <script> tag option is nice when you want to define your templates in the HTML, give them an id and reuse them. You can also create templates from strings, which gives you the ability to create them on the fly in code or even pull them from a data store.
The render method is used to render HTML content from data using a template. A set of data can be rendered with a template declared in a <script> tag using the syntax $(“#myTmpl”).render(data). For example, you could render a list of movies with a template using the following code:
// #1: Render the my.vm data using the scriptTmpl from a script tag
var htmlString = $("#scriptTmpl").render(my.vm);
// Insert the htmlString into the DOM
$("#div1").html(htmlString);
You can also compile a template from a string using the $.templates(tmplString) function and set it to a variable. You can then render the compiled template, as shown here:
// #2: Compile a template from a string, return a compiled template
var tmpl2 = $.templates(tmplString);
htmlString = tmpl2.render(my.vm);
$("#div2").html(htmlString);
You can also compile a template from a string using the $.templates(name, template) syntax, as shown here:
// #3: Compile a template, name and register it
$.templates("myTmpl3", tmplString);
var htmlString = $.render.myTmpl3(my.vm);
$("#div3").html(htmlString);
In this example, the $.templates function compiles a template using the tmplString string and registers it as a named template. The template can then be accessed by name and rendered using the $.render.name() syntax.
The $.templates function is similar to jQuery methods such as .css, or .attrib in that it provides an alternative syntax for registering and compiling multiple templates in a single call. Instead of passing two parameters (name and templateString), you can pass just one parameter consisting of a mapping object with key/value pairs for each template that’s to be registered:
// #4: Compile multiple templates, register them and render
var tmplString2 = "<div>*** {{:movies.length}} Total Movies ***</div>";
$.templates({
tmpl4a: tmplString,
tmpl4b: tmplString2
});
htmlString = $.render.tmpl4a(my.vm);
htmlString += $.render.tmpl4b(my.vm);
$("#div4").html(htmlString);
Every property in the object becomes a named and registered template that can be rendered. The value of the property is the string that will become the template.
You have many options to create, register and render templates. Defining templates in script tags is a common approach for most scenarios. However, creating templates from strings offers a lot of flexibility. The preceding expanded syntax provides even more flexibility for associating other features with named templates (such as declaring helper functions specific to the template). All of these examples can be found in 03-rendering-templates.html in the code download.
JsRender Fundamentals
JsRender templates consist of HTML markup plus JsRender tags, such as the {{for …}} tag or the {{: …}} tag. Figure 1 shows the syntax for the most basic of JsRender tags: the {{: }} and {{> }} tags. All JsRender template tags are wrapped with double curly braces. The tag name (in this case the “:” or “>” character) can be followed by one or more parameters or expressions. (In the case of the {{: }} tag, the result of the expression would then be rendered.) Once a template has been defined and there’s data to render in that template, it can be rendered.
Figure 1 Basic JsRender Syntax
Description | Example | Output |
Value of firstName property of the data item with no encoding | {{:firstName}} | Madelyn |
Simple object path to nested property, with no encoding | {{:movie.releaseYear}} | 1987 |
Simple comparison | {{:movie.releaseYear < 2000}} | true |
Value with no encoding | {{:movie.name}} | Star Wars IV: Return of the Jedi |
HTML-encoded value | {{>movie.name}} | Star Wars: Episode VI: <span style='color:purple;font-style: italic;'>Return of the Jedi</span> |
HTML-encoded value | {{html:movie.name}} | Star Wars: Episode VI: <span style='color:purple;font-style: italic;'>Return of the Jedi</span> |
The following code contains an HTML element named movieContainer (this is where the template will be rendered):
<div id="movieContainer" class="resultsPanel movieListItemMedium"></div>
<script id="movieTemplate" type="text/x-jsrender">
<div class="caption">{{:name}}</div>
<div class="caption">{{>name}}</div>
<img src="{{:boxArt.smallUrl}}"/>
<div class="text">Year Released: {{:releaseYear}}</div>
<div class="text">Rating: {{:rating}}</div>
</script>
The preceding code also contains the template named movieTemplate, which defines a div to display the name of the movie using no HTML encoding using the syntax {{:name}}. The title in the sample data may contain HTML elements, so to render elements containing HTML it’s important not to use encoding. However, if you want to render the encoded HTML, you can use the syntax with the > character or use HTML (as shown in Figure 1).
The name property value contains HTML elements, so for demonstration purposes only, the preceding code displays the name property’s value with no encoding ( {{:name}} ) and then shows the HTML-encoded value ( {{>name}} ). You can run the full example in 04-render-values.html in the code download.
The sample movie data passed to the template has a property for boxArt, which in turn has a property for the smallUrl of the image. The img tag src is set by diving into an object graph’s hierarchy using the dot syntax boxArt.smallUrl. Paths can also be traversed using square brackets, so instead of writing boxArt.smallUrl, the same result code be achieved using boxArt['smallUrl'].
It’s important to note that JsRender syntax can also be used to render other values such as the names of classes or ids for HTML elements.
JsRender template rendering detects whether the data parameter is an array or not. If it’s an array, the return value is the concatenation of the strings that would result from passing each of the individual array items to the render method. So the template will be rendered once for each data item and the result will be the concatenated string.
The following code demonstrates how a single movie object from the array is rendered to the movieTemplate (the full source is available in sample 04-render-values.html):
my.vm = { movies: getMovies() };
$("#movieContainer").html($("#movieTemplate").render(my.vm.movies[1]));
The next section will demonstrate how the template could be written to iterate through an array, as well.
Drilling into Hierarchical Data
Templates are often used to render a series of items, which can often contain nested and hierarchical data (object graphs). Figure2 shows how JsRender can iterate through a data series using the {{for}} tag. The parameter for the {{for}} tag can be an array or a series of arrays, which will be iterated.
Figure 2 Iterating Basics
Description | Example | Output |
Loop through each item of an array, using “for” | {{for cast}} <div>{{:stageName}}</div> {{/for}} |
Landon Papa Ella Papa |
Access the data context using #data | {{for phone}}<div>{{:#data}}</div>{{/for}} | 555-555-1212 555-555-1212 |
The sample movie data defines that each movie has an array of rating stars called RatingStars. The property contains the CSS class to display a rating star for the movie. The following code (from 05-for-data.html in the sample code) demonstrates how to iterate through each item in the RatingStars array and render the CSS class name using the {{:#data}} syntax:
<ul>
{{for ratingStars}}
<li class="rating {{:#data}}"/>
{{/for}}
</ul>
The #data token is a JsRender keyword that represents the object being iterated. In this case, the RatingStars array contains a string array, so #data will represent a string. The output for this sample is shown in Figure 3, where the rating stars are displayed beside the movie’s image.
Figure 3 Rendering an Object with and Without Encoding
Arrays to Templates
When an array is passed to a template, the template is rendered once for each item in the array. A template renders against a single data item, but if it includes a {{for}} template tag with an array parameter, then the section of the template between the opening and closing tags {{for}} and {{/for}} will be further iterated. The following code will pass an object my.vm that contains an array of movies to the render function to display a list item for each movie (the complete source can be found in the sample 06-if-else.html):
$("#movieList").html($("#movieTemplateMedium").render(my.vm));
The template will be rendered as content of the <ul> element with the id movieList:
<ul id="movieList"></ul>
Figure 4shows the template movieTemplateMedium begins by rendering
- . For every movie in the array, an
- will be rendered under the container’s
.
The template in Figure 4 receives the my.vm object as its context and then loops through the movies array property because of the {{for movies}} code. If the template received the array my.vm.movies instead of my.vm, the {{for}} {{/for}} block could be removed, as the template would then execute for every item in the array.
Figure 4 Rendering a List of Items and Using Conditionals
{{for movies}}
<li class="movieListItemMedium">
<div class="caption">{{:name}}</div>
{{if boxArt.smallUrl}}
<img src="{{:boxArt.smallUrl}}"/>
{{else}}
<img src="../images/icon-nocover.png"/>
{{/if}}
<br/>
<div class="text">Year Released: {{:releaseYear}}</div>
<div class="text">rating: {{:rating}}</div>
<ul>
{{for ratingStars}}
<li class="rating {{:#data}}"/>
{{/for}}
</ul>
<br/>
<div class="text">${{:salePrice}}</div>
{{if fullPrice !== salePrice}}
<div class="text highlightText">PRICED TO SELL!</div>
{{/if}}
</li>
{{/for}}
Traversing Paths
As we’ve seen previously, paths can be traversed using the dot syntax or the square brackets. However, you can also go back up an object hierarchy using #parent or identify an array element using square brackets. For example, you could replace the code that displays the img tag in the template with following:
<img src="{{:#parent.parent.data[2].boxArt.smallUrl}}"/>
The context for the template is an item in the my.vm.movies array. So by navigating up through the parent twice, the context will become the my.vm object. Then you can grab the movie with index 2 and use its boxArt.smallUrl property for the image. The result of this would be to display the same movie (the third movie) for every movie in the array (as shown in Figure 5). Not exactly ideal in this situation, but it serves the point of showing how to traverse objects both up and down the hierarchy.
Figure 5 Traversing Paths to Display the Same Movie Image for Every Movie
Conditionals
The JsRender syntax also support conditionals using the tags {{if}} and {{else}} (shown in Figure 6). The {{if}} tag may be followed by zero, one or more {{else}} tags, and then a closing {{/if}} tag. The content of the block between the {{if}} tag and the {{/if}} (or up to the first {{else}} tag if one exists) will be rendered in the output only if the value of the expression in the {{if}} is “truthy.”
Figure 6 Conditionals, Expressions and Operators
Description | Example | Output |
Creating an if block evaluated on a comparison | {{if fullPrice !== salePrice}} <div class="text highlightText"> PRICED TO SELL!</div> {{/if}} |
PRICED TO SELL! |
An if/else block | {{if qtyInStock >= 10}} In Stock {{else qtyInStock}} Only {{:qtyInStock}} left! {{else}} Not available. {{/if}} |
Only 5 left! |
The {{else}} tag can act like an ifelse when it includes an expression of its own. Notice the second example in Figure 6, which demonstrates a conditional with two else tags. This code evaluates three conditions in sequence.
The block following an {{else someCondition}} tag will be output if the condition evaluates to true (or, more precisely, truthy). This syntax can be used effectively like a switch/case statement to evaluate n number of conditions.
The expressions used in JsRender tags can be complex expressions using evaluation operators including paths, strings, numbers, function calls and comparison operators such as ===, !== and <=. This is the case both for the expressions evaluated and rendered by the {{: }} tag, and, here, for the conditional expressions used in the {{if}} and {{else}} tags.
Using Counters and Complex Conditional Expressions
Sometimes it’s necessary to display or use a counter variable in a template. One such scenario is when you want to display a row number in the template. Another such scenario might be that you want to assign a unique id to each element in a template so you can reference it later. For example, you iterate through a list of movies and you wrap them all inside a <div> tag. You could assign the id of the div tag to be movieDiv1, movieDiv2 and so on. Then you could find the element by its id when needed, perhaps to bind it to an event handler using the jQuery delegate function. For both of these scenarios, you can use the JsRender keyword #index (which starts at 0). As shown in Figure 7, #index can be displayed in a template quite easily.
Figure 7 Counters and Conditionals in Multiple Expressions
Description | Example | Output |
Using an index to count inside of a template | {{for movies}} <div class="caption"> {{:#index}}: {{:name}} </div> {{/for}} |
3: The Princess Bride |
Using logical operators | {{if stars.length || cast.length}} <div class="text">Full Cast:</div> {{/if}} |
Full Cast: |
Oftentimes a single expression may not be adequate. JsRender supports multiple expressions using logical operators such as || and && (“or” and “and,” respectively). This makes it easy to combine multiple expressions when you want to evaluate either one of them to be true.
Iterate Multiple Arrays Together
The {{for}} tag can also iterate through multiple arrays at once. This is helpful when there are two arrays that need to be traversed together. For example, the following code demonstrates how the for tag will iterate through both the Stars and the Cast arrays together:
{{for stars cast}}
<div class="text">{{:name}}</div>
{{/for}}
If there are two items in the Stars array and three in the Cast array, all five items’ content will be rendered.
This is a good situation to use the #data keyword, too, if the items in the arrays aren’t objects (with properties) but simple string values. For example, the arrays could be MobilePhones and HomePhones string arrays, and you might want to list them in a single list.
Nested Templates
Applications often retrieve object graphs where objects have properties whose values are arrays of objects that may also contain other arrays of objects. These scenarios can quickly become difficult to manage when nesting multiple {{for}} statements. Consider a customer object may have orders, which have order items, which have a product, which has warehouses. This hierarchical data could be rendered by using multiple {{for}} tags. This is a great situation where the content of a {{for}} block can be packaged as a separate template and rendered as a nested template. This can reduce code and make it more easily readable and maintainable, and provide for reuse and modularity of the templates.
You can use a nested template with a {{for}} tag by removing the block content (making it self-closing) and specifying an external template using the tmpl parameter. For example, you can set the tmpl parameter to be the name of a named template (registered using $.templates) or you can use a jQuery selector for a template declared in a script block. The following syntax uses the named template myTmpl for each item in the array named myArray:
{{for myArray tmpl="myTmpl"/}}
The following code snippet will use the template with the id of movieTemplateMedium for each item in the movies array (from the sample 04-tmpl-combo-iterators.html):
<ul>
{{for movies
tmpl="#movieTemplateMedium"/}}
</ul>
You can iterate through multiple arrays and apply a template to them, as well. For example, the following code snippet will use the castTemplate for each item in both the Stars and Cast properties (which are both arrays from sample 07-tmpl-combo-iterators.html):
<ul>
{{for stars cast tmpl="#castTemplate"/}}
</ul>
Notice that in each of the previous code snippets the {{for}} tag is a self-closing tag rather than a block tag containing content. Instead of iterating over its block content, it’s deferring the iteration to the nested template indicated by the tmpl property and is rendering that template once for each item in the array.
The {{if}} template tag can also be used as a self-closing tag referencing another template for its content, just like the {{for}} tag. For example, the following code would render the named template myAddressTmpl if address is truthy:
{{if address tmpl="myAddressTmpl"/}}
The samples available for download demonstrate all of the features discussed in this article. Figure 8 shows the list of movies rendered using all of these features from the sample 07-tmpl-combo-iterators.html file.
Figure 8 Putting It All Together
More Under the Covers
This column demonstrates the basic features of JsRender, but there’s much more under the covers. For example, although the conditional tags can contain multiple {{for}} tags with conditions (such as a switch/case statement), there might be cleaner ways to handle this situation, such as using custom tags. JsRender supports custom tags for complex logic, helper functions, navigating up the object graph using paths, customer converter functions, allowing JavaScript code within templates, encapsulation of templates and more. I’ll explore some of these techniques in the next issue.
John Papa is a former evangelist for Microsoft on the Silverlight and Windows 8 teams, where he hosted the popular Silverlight TV show. He has presented globally at keynotes and sessions for the BUILD, MIX, PDC, TechEd, Visual Studio Live! and DevConnections events. Papa is also a Microsoft Regional Director, a columnist forVisual Studio Magazine (Papa’s Perspective) and an author of training videos with Pluralsight. Follow him on Twitter at twitter.com/john_papa.
Thanks to the following technical expert for reviewing this article: Boris Moore