Introduction
The
jQuery Templates plugin is a “template engine” that works on the client side as a
jQuery extension.
This plugin helps to show data in the browser in JavaScript objects and arrays, saving you from the routine operations of creating HTML-code, escaping special characters, etc. In addition, it has very interesting features - for example, it allows you to update the HTML code created with it when the source data changes.
Of course,
jQuery Templates is not the only and not the first “template engine”, but it has a big advantage over alternative options - support from the
jQuery Team . This allows us not to be afraid of the fact that this plugin will be abandoned, and various problems that arise when new versions of browsers are released will have to be resolved on our own.
')
In this article, I will discuss the main features of
jQuery Templates and demonstrate its work in various scenarios, and in subsequent articles I will discuss functions not included in the core code of the plugin (
jQuery Templates Plus ) and on the extension of the template language.
A bit of history
This plug-in was developed by Microsoft’s
JavaScript- based
Micro-templating library (by
John Resig ), some details about the development can be found in
Stephen Walther ’s blog post “
An Introduction to jQuery Templates ” and from
John Resig ’s
Templating Syntax post on
jQuery forum. Since last October,
jQuery Templates ,
jQuery DataLink, and
jQuery Globalization plug-ins have become
part of the jQuery project .
Part one, theoretical
Getting Started
Let's start. The following example shows the list of movies specified in the array (the full code for the example is in the
BasicSample1.htm file):
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <title> (1)</title> <link href="Styles/Default.css" rel="Stylesheet" type="text/css" /> <script src="Scripts/jquery-1.5rc1.js" type="text/javascript"></script> <script src="Scripts/jquery.tmpl.js" type="text/javascript"></script> <script src="DataItems.js" type="text/javascript"></script> <script id="movieTmpl" type="text/x-jquery-tmpl"> <div class="movie-bag"> <img src="Content/Thumbnails/${thumbnail}" class="thumbnail" /> <div class="base-info"> <h2> ${title} </h2> <p> : ${director}<br /> : ${actors}<br /> : ${year} </p> </div> </div> </script> <script type="text/javascript"> $(function () { $('#movieTmpl').tmpl(dataItems).appendTo('#movieListBag'); }); </script> </head> <body> <h1> (1)</h1> <div id="movieListBag"> </div> </body> </html>
But what you see in the browser:
Let's analyze this example in detail.
So the first thing I do is connect the
jQuery Core Library and
jQuery Templates :
<script src="Scripts/jquery-1.5rc1.js" type="text/javascript"></script> <script src="Scripts/jquery.tmpl.js" type="text/javascript"></script>
It has been repeatedly stated that
jQuery Templates will be included in the
jQuery Core Library - but in jQuery 1.5 RC1, released on January 24, there are still no templates.
Then I upload a list of movies:
<script src="DataItems.js" type="text/javascript"></script>
Of course, you can prepare the source data in any way that is convenient for you - get it using an AJAX request, create it based on user input, etc., I use the static script only as an example.
Inside the
DataItems.js file looks like this:
var dataItems = [ { title: '', thumbnail: 'Bandits.jpg', director: ' ', actors: [' ', ' ', ' '], year: 2001, budget: 95000000, grossRevenue: 67631903, rating: 0, frames: ['Bandits-1.jpg', 'Bandits-2.jpg', 'Bandits-3.jpg', 'Bandits-4.jpg', 'Bandits-5.jpg'] }, ...
The next step I create the template:
<script id="movieTmpl" type="text/x-jquery-tmpl"> <div class="movie-bag"> <img src="Content/Thumbnails/${thumbnail}" class="thumbnail" /> <div class="base-info"> <h2> ${title} </h2> <p> : ${director}<br /> : ${actors}<br /> : ${year} </p> </div> </div> </script>
Please note that the template is placed in the
SCRIPT tag, and as the MIME type, I specify
text / x-jquery-tmpl . Having encountered an unfamiliar MIME type while parsing a document, the browser does not attempt to interpret the contents of the
SCRIPT tag, which is what I need.
Generally speaking, a template can be placed in any tag, for example, in a
DIV tag:
<div id="movieTmpl" style="display: none"> <div class="movie-bag"> <img src="Content/Thumbnails/${thumbnail}" class="thumbnail" /> <div class="base-info"> <h2> ${title} </h2> <p> : ${director}<br /> : ${actors}<br /> : ${year} </p> </div> </div> </div>
However, in this case, you can not avoid side effects, because the browser will definitely try to interpret the template code.
For example, for the example above, an attempt will be made to load a non-existent image:
But with the table, everything can be much more interesting (many thanks to
TEHEK for this example!):
<div id="movieTmpl" style="display: none"> <table> <tbody> {{each dataItems}} <tr> <td>${title}</td> <td>${director}</td> <td>${year}</td> </tr> {{/each}} </tbody> </table> </div>
Internet Explorer and Opera will handle this code correctly:
But Chrome and Fire Fox "push" the extra code out of the table, resulting in a table that will be empty ... Happy debugging! ;-)
For the
SELECT tag, a similar pattern will be observed.
I recommend placing templates in the
DIV tags during development in order to take advantage of all the charms of IntelliSence and then move them to the
SCRIPT tag.
Finally, I instantiate the template using the following call:
$('#movieTmpl').tmpl(dataItems).appendTo('#movieListBag');
What happens when this happens, I depicted in the diagram below:
So:
- The .tmpl () method gets the template text - i.e. inner text of an element received by calling $ ('# movieTmpl') .
- The text of the template is compiled - on its basis a JavaScript function is created.
- A “template instance” is created - an object that contains a reference to a data element ( data field), passed as an argument to the .tmpl () method. The .tmpl () method can be passed an array, an object, null, or called without any arguments. If you pass an array, then for each element of the array you will create your own copy of the template that refers to this element, in all other cases only one instance will be created.
- The compiled template function is called, to which the instance object is passed. The function returns the text of the template in which all substitutions are made.
- The text obtained in the previous step is converted to a collection of HTML elements. References to these elements are also stored in the instance object (the nodes field), which makes it possible in the future to easily update the “output” of the template when the source data changes (see the Dynamic Update section).
- Finally, the .tmpl () method returns a jQuery collection of HTML elements that are added to the document by calling appendTo ('# movieListBag') .
Expressions
For substitution in the template of values the tag
$ {...} is used . Inside this tag, you can specify both the name of the property of the object passed to the
.tmpl () method, and any valid JavaScript expression, including the function call.
Using object properties (array element):
<h2> ${title} </h2>
Using JavaScript Expressions:
<p> : $${(budget / 1000000).toFixed(0)} .<br /> : $${(grossRevenue / 1000000).toFixed(1)} . </p>
Template instance fields and methods
Inside expressions, you can access the current template instance via the
$ item variable, and to refer to the current data item, the
$ data variable.
Each template instance contains the following fields:
- data - contains a link to the data element associated with the template instance;
- tmpl - contains a link to the compiled template used for rendering;
- parent - if the template was called from another template using the {{tmpl}} tag, contains a link to the "parent" instance of the template;
- nodes - after rendering, contains links to HTML elements generated as a result of applying the template.
In addition, the
.tmpl () method takes two arguments -
data and
options . You have already met the
data argument, and a link to the data element is passed through it. And using the
options argument, you can pass a reference to the object, all fields and methods of which will be transferred to each instance of the template created in the
.tmpl () method.
The following is an example of using this parameter:
<script id="movieTmpl" type="text/x-jquery-tmpl"> <div class="movie-bag"> <img src="Content/Thumbnails/${thumbnail}" class="thumbnail" /> <div class="base-info"> <h2> ${title} </h2> <p> : ${director}<br /> : ${actors}<br /> : ${year}<br /> : $${$item.formatBudget(budget)} .<br /> : $${$item.formatGrossRevenue(grossRevenue)} . </p> </div> </div> </script>
$(function () { $('#movieTmpl') .tmpl( dataItems, { formatBudget: function (value) { return (value / 1000000).toFixed(0); }, formatGrossRevenue: function (value) { return (value / 1000000).toFixed(1); } }) .appendTo('#movieListBag'); });
In this example, I use function calls for formatting budget values and fees, but in order not to clutter up the global namespace, I passed them through the
options parameter, after which these functions became available as methods of the current template instance.
And finally, an instance of the template contains the
update () and
html () methods, the use of which I will show below.
What does the compiled template look like?
You can see what a compiled template looks like by using the
.template () method, which performs the compilation of templates. This method returns a function object whose contents are easy to see:
$('#compiledTemplateBag').text('' + $('#movieTmpl').template());
The template used in the example above, after compilation is as follows (the text is formatted for better readability):
function anonymous(jQuery, $item) { var $ = jQuery, call, _ = [], $data = $item.data; with ($data) { _.push('<div class="movie-bag"> <img src="Content/Thumbnails/'); if (typeof (thumbnail) !== 'undefined' && (thumbnail) != null) { _.push($.encode((typeof (thumbnail) === 'function' ? (thumbnail).call($item) : (thumbnail)))); } _.push('" class="thumbnail" /> <div class="base-info"> <h2> '); if (typeof (title) !== 'undefined' && (title) != null) { _.push($.encode((typeof (title) === 'function' ? (title).call($item) : (title)))); } _.push(' </h2> <p> : '); if (typeof (director) !== 'undefined' && (director) != null) { _.push($.encode((typeof (director) === 'function' ? (director).call($item) : (director)))); } _.push('<br /> : '); if (typeof (actors) !== 'undefined' && (actors) != null) { _.push($.encode((typeof (actors) === 'function' ? (actors).call($item) : (actors)))); } _.push('<br /> : '); if (typeof (year) !== 'undefined' && (year) != null) { _.push($.encode((typeof (year) === 'function' ? (year).call($item) : (year)))); } _.push(' </p> </div> </div>'); } return _; }
I think that now it should be clear to you how the expressions specified in the
$ {...} tag are processed - and this understanding can greatly help you when debugging! The fact is that
jQuery Templates performs a relatively simple transformation of the template text, so if you make a mistake in an expression, the error message will relate to the text of the resulting function conversion and can often be extremely obscure.
Unfortunately, if the template is compiled with an error, then without special tweaks you will not be able to see the text of the function with an error, since the corresponding method is declared private.
Well, perhaps, this is the story about the work of
jQuery Templates should be completed and proceed to its practical application.
Part two, practical
Conditions
In order to apply parts of the template depending on certain conditions,
jQuery Templates uses
{{if}} ... {{else}} ... {{/ if}} tags.
The following example shows the use of these tags (the complete example code is in the
IfElseTag.htm file):
<p> : {{if $item.data.media == 'dvd'}} <img src="Images/media-dvd.png" /> {{else $item.data.media == 'blue-ray'}} <img src="Images/media-blueray.png" /> {{else}} <img src="Images/media-unspecified.png" /> {{/if}} </p>
As it is easy to guess, this code is designed to display the icon of the media on which the film is located. Here’s what it looks like in a browser:
As a condition in tags
{{if}} and
{{else}} you can use any valid JavaScript expression.
Collection processing
To handle collections in templates, the
{{each}} ... {{/ /}} tag is used. The following example shows the use of the
{{each}} tag to display a list of actors (the full example code is in the file
EachTag1.htm ):
: {{each actors}} ${$value} {{if $index < $data.actors.length - 1}} , {{/if}} {{/each}}
In the browser, this example looks like this:
As an argument, the
{{each}} tag can be passed an array, an object, or a
jQuery collection. Inside, the
{{each}} tag uses the
jQuery.each () call, so everything that is said in the
documentation about the jQuery.each () behavior also holds for the
{{each}} tag. The example below demonstrates the use of the
{{each}} tag to display all properties of an object (the full code for the example is in the file
EachTag2.htm ):
<script id="objTmpl" type="text/x-jquery-tmpl"> <div> <dl> {{each $data}} <dt> ${$index} </dt> <dd> ${$value} </dd> {{/each}} </dl> </div> </script>
Inside the
{{each}} tag, two variables are available:
$ value , which contains a reference to the current element of the array, and
$ index , which contains the index of the current element of the array or the name of the property.
Of course, you can use other tags inside the
{{each}} tag, and besides, you will still have access to the
$ item and
$ data variables. In the above example, the variables
$ index and
$ data together with the
{{if}} tag are used to display a comma between the names of the actors - unfortunately, the
$ last variable is not provided, although it would be very useful!
Finally, if you suddenly have such a need, you can change the default variable names. In the example below, these names are changed to
myIndex and
myValue (the full code of the example is in the file
EachTag3.htm ):
: {{each(myIndex, myValue) actors}} ${myValue} {{if myIndex < $data.actors.length - 1}} , {{/if}} {{/each}}
By the way, an attempt to change the name only for the
$ index variable will not lead to anything good - there will be no error, but you will not be able to get access to the current value either!
Nested templates
Templates can be very large - and then it makes sense to divide them into several smaller parts or include duplicate parts, which are logical to highlight in a separate template. In
jQuery Templates, this is done using nested templates, which are called using the
{{tmpl}} tag.
The example below illustrates how to move part of the template code to another template (the full example code is in the
NestedTemplates1.htm file):
<script id="movieTmpl" type="text/x-jquery-tmpl"> ... <p> : ${director}<br /> : {{tmpl '#actorsTmpl'}}<br /> : ${year} </p> ... </script> <script id="actorsTmpl" type="text/x-jquery-tmpl"> {{each actors}} ${$value} {{if $index < $data.actors.length - 1}} , {{/if}} {{/each}} </script>
The
{{tmpl}} tag
must contain the jQuery- selector of the template being called or the name of the template previously stored in the cache. Because in this example, the
{{tmpl}} tag has no other arguments, the nested template will receive the same data element as the parent - but it will have its own template instance, and the
parent field in it will refer to the parent template instance.
The following example demonstrates sending a new data element to a nested template and using a reference to the parent instance of the template. As in the case of using the
.tmpl () method, if you specify an array when invoking a nested template, the template will be applied to each element of the array (the full code of the example is in the
NestedTemplates2.htm file):
<script id="movieTmpl" type="text/x-jquery-tmpl"> ... <p> : ${director}<br /> : {{tmpl(actors) '#actors_template'}}<br /> : ${year} </p> ... </script> <script id="actors_template" type="text/x-jquery-tmpl"> ${$data} {{if $data !== $item.parent.data.actors[$item.parent.data.actors.length - 1]}} , {{/if}} </script>
And finally, the last example in this section shows how to pass the
options argument to the nested template, and at the same time demonstrates how the
options argument can be used to determine the last element in the array being processed (the full example code is in the
NestedTemplates3.htm file):
<script id="movieTmpl" type="text/x-jquery-tmpl"> ... <p> : ${director}<br /> : {{tmpl(actors, { last: actors[actors.length - 1] }) '#actors_template'}}<br /> : ${year} </p> ... </script> <script id="actors_template" type="text/x-jquery-tmpl"> ${$data} {{if $data !== $item.last}} , {{/if}} </script>
Transformation
Another interesting feature of
jQuery Templates is related to the transformation of HTML markup, for which the
{{wrap}} tag is used (generally speaking, wrap is “wrapping”, but it seems to me that the term “transformation” reflects the essence of what is happening).
A classic example of using the
{{wrap}} tag is creating bookmarks:
Here’s how it looks inside (the full code of the example is in the
Transformation1.htm file):
<script id="tabsContent" type="text/x-jquery-tmpl"> {{wrap(null, { viewState: $item.viewState }) '#tabsTmpl'}} <h1>English</h1> <div> <h2>The Ballad of East and West</h2> <h3>Rudyard Kipling</h3> <p>OH, East is East, and West is West...</p> </div> {{/wrap}} </script>
The initial data for the transformation is placed in the
tabContent template - it is this template that I will continue to instantiate.
The HTML markup that I will transform is placed in the
{{wrap}} tag. For the
{{wrap}} tag, exactly the same call notation is used as for the
{[tmpl}} tag — i.e., you must specify the selector or the name of the template and you can additionally pass a reference to the data element and options. In this case, in the
options parameter, I pass a link to the
viewState object that contains the index of the selected bookmark.
The code for the transformation is as follows:
<script id="tabsTmpl" type="text/x-jquery-tmpl"> <div class="tab-head"> {{each $item.html("h1", true)}} <div class="tab {{if $index == $item.viewState.index}}active{{/if}}"> ${$value} </div> {{/each}} </div> <div class="tab-body"> {{html $item.html("div")[$item.viewState.index]}} </div> </script>
Here
DIV.tab-head contains tabs for the tabs, and
DIV.tab-body contains the contents of the selected bookmark.
Calling
$ item.html ("h1", true)} fetches the data. The first parameter (
filter ) sets the
filter for the first level elements , and the second parameter (
textOnly ) indicates that I want to get only the
inner text of each selected element - i.e. in this case, I'll get a collection of strings, each of which will contain the text from the corresponding
H1 tag. From this collection of lines, I create bookmark shortcuts.
I am a little upset by the fact that I cannot specify an arbitrary selector in the html () method - but, fortunately, no one bothers me to use any jQuery selectors on the results of this method.
Another funny moment is connected with the way the selector is set - when I try to set the selector in my favorite single quotes when I compile the template, an error occurs.
The next step I choose from the source data
DIV , corresponding to the active tab, and put it in
DIV.tab-body . Please note that in this case I use the
{{html}} tag, and not
$ {...} - the fact is that when using the
$ {...} tag, special characters are escaped, but for me this is in this case not necessary.
Finally, I instantiate the template:
var viewState = { index: 0 }; $('#tabsContent').tmpl(null, { viewState: viewState }).appendTo('#tabsBag');
And I install handlers for clicking on bookmarks:
$('#tabsBag').delegate('.tab', 'click', function () { var item = $.tmplItem(this); item.viewState.index = $(this).index(); item.update(); });
What happens in the click handler? First, by calling
$ .tmplItem (this), I get a link to the template instance associated with the current item. Then, I change the index of the selected bookmark - remember that we have in every instance of the template a reference to the
viewState object? And in the final, I call the
update () method of the template instance, which causes the template to be re-rendered, but with a different value in
viewState .
The following example is more complex, but also more useful.
Its first difference from the previous example is that I dynamically create the initial data for the transformation (the full code of the example is in the file
Transformation2.htm ):
<script id="movieListTmpl" type="text/x-jquery-tmpl"> {{wrap(null, {viewState: $item.viewState}) '#tabsTmpl'}} {{tmpl(dataItems) '#movieTmpl'}} {{/wrap}} </script> <script id="movieTmpl" type="text/x-jquery-tmpl"> <div class="movie-bag"> <img src="Content/Thumbnails/${thumbnail}" class="thumbnail" /> ... </div> </script>
var viewState = { index: 0 }; $('#movieListTmpl').tmpl({ dataItems: dataItems }, { viewState: viewState }).appendTo('#tabsBag');
The second difference is because tags with the name of bookmarks are now not on the first level; when creating labels, I have to use
jQuery selectors to get this information:
<script id="tabsTmpl" type="text/x-jquery-tmpl"> <div class="tab-head"> {{each $item.html("div")}} <div class="tab {{if $index == $item.viewState.index}}active{{/if}}"> ${$('h2', $value).text()} </div> {{/each}} </div> <div class="tab-body"> {{html $item.html("div")[$item.viewState.index]}} </div> </script>
In the browser, this example will look like this:
Pattern caching
Each call
$ ('# ...'). Tmpl (...) compiles the template, which, despite the dramatically increased speed of JavaScript in modern browsers, can adversely affect performance. The developers of
jQuery Templates could not get round this obvious fact with their attention, so
jQuery Templates provides a mechanism for precompiling and caching templates.
Compiling and caching the template is as follows:
$('#movieTmpl').template('movieTmpl');
The compiled template is stored in the
jQuery Templates internal cache under the name
movieTmpl . The
jQuery.tmpl () method is used to access the cached template, and the first parameter is the name of the cached template:
$.tmpl('movieTmpl', dataItems).appendTo('#movieListBag');
In the example below, you navigate through the movie list, and a cached template is used to display movie information.
The template code practically does not differ from those I used earlier, the only difference is that under the description of the film additional navigation links are placed (the full code of the example is in the
CachedTemplates.htm file):
<script id="movieTmpl" type="text/x-jquery-tmpl"> <div class="movie-bag"> <img src="Content/Thumbnails/${thumbnail}" class="thumbnail" /> <div class="base-info"> <h2> ${title} </h2> <p> : ${director}<br /> : ${actors}<br /> : ${year} </p> </div> </div> <div> {{if $item.canMoveBack}} <a href="javascript:" class="nav-link" x-inc="-1">[]</a> {{/if}} {{if $item.canMoveFwd}} <a href="javascript:" class="nav-link" x-inc="1">[]</a> {{/if}} </div> </script>
The accompanying script is also simple:
var movieIndex = 0; $(function () { $('#movieTmpl').template('movieTmpl'); updateMovie(); $('#movieBag').delegate('.nav-link', 'click', function () { movieIndex += parseInt($(this).attr('x-inc')); updateMovie(); }); }); function updateMovie() { $('#movieBag').empty(); $('#movieBag').append( $.tmpl('movieTmpl', dataItems[movieIndex], { canMoveBack: movieIndex > 0, canMoveFwd: movieIndex < dataItems.length - 1 })); }
The clicker on the navigation link changes the index of the selected movie, and then calls the
updateMovie () function, which first clears the container with the description of the movie, and then fills it with new data.
Here’s how this example looks in a browser:
Dynamic template loading
Unfortunately, the code shown below will not work:
<script id="movieTmpl" src="Templates/DynamicLoading.htm" type="text/x-jquery-tmpl"></script>
The browser, of course, will download the corresponding file - but you still will not be able to access its contents.
But the template can still be placed in a separate file, and this will require literally one additional line of code (the full code of the example is in the DynamicLoading.htm file ): $(function () { $.get('Templates/DynamicLoading.htm', {}, function (templateBody) { $.tmpl(templateBody, dataItems).appendTo('#movieListBag'); }); });
Because
in this case, we get a template in the form of text, to instantiate it, the jQuery.tmpl () method is used , the first argument to which the resulting template text is passed.Yes, the jQuery.tmpl () method is used to instantiate both cached templates by name and templates specified as text (tradition! ..) - however, it is smart enough to distinguish them from each other.If you need to load several related templates, you can use the WaitSync library (see “ Synchronizing Asynchronous Calls. WaitSync ”) or write your own synchronizer (the full code of the example is in the DynamicLoading2.htm file ): $(function () { var ws = new WaitSync(function () { $.tmpl('movieTmpl', dataItems).appendTo('#movieListBag'); }); $.ajax({ cache: false, url: 'Templates/MovieTmpl.htm', success: ws.wrap('MovieTmpl', function (templateBody) { $.template('movieTmpl', templateBody); }), error: ws.wrap('MovieTmpl', function () { alert('Error loading MovieTmpl.htm!'); }) }); $.ajax({ cache: false, url: 'Templates/ActorsTmpl.htm', success: ws.wrap('ActorsTmpl', function (templateBody) { $.template('actorsTmpl', templateBody); }), error: ws.wrap('ActorsTmpl', function () { alert('Error loading ActorsTmpl.htm!'); }) }); });
Note that in this case, the actorsTmpl template is called by name (file Templates \ MovieTmpl.htm ): <p> : ${director}<br /> : {{tmpl(actors, { last: actors[actors.length - 1] }) 'actorsTmpl'}}<br /> : ${year} </p>
Dynamic update
In the last section of the practical part, I will show two more scenarios for jQuery Templates — changing the associated data and replacing the associated template.In the example below, the ability to change its rating has been added for each movie (the full code of the example is in the DynamicUpdate1.htm file ): <script id="movieTmpl" type="text/x-jquery-tmpl"> <div class="movie-bag"> <img src="Content/Thumbnails/${thumbnail}" class="thumbnail" /> <div class="base-info"> ... <p> : <img src="Images/rating-down.png" alt="" title="-1" class="rating-button" x-inc="-1" /> ${rating} <img src="Images/rating-up.png" alt="" title="+1" class="rating-button" x-inc="1" /> </p> </div> </div> </script>
Companion Code: $(function () { $('#movieTmpl').tmpl(dataItems).appendTo('#movieListBag'); $('#movieListBag').delegate('.rating-button', 'click', function () { var item = $.tmplItem(this); item.data.rating += parseInt($(this).attr('x-inc')); item.update(); }); });
As you can see, this code is very similar to the bookmarks code from the “Transformation” section, only when working with bookmarks, I accessed the viewState shared object , and here I work with data associated with an instance of the template.In the browser, this example looks like this:The following example demonstrates the substitution of the associated template (the full code of the example is in the file DynamicUpdate2.htm ): <script id="movieShortTmpl" type="text/x-jquery-tmpl"> <div class="movie-bag"> {{tmpl '#movieMainTmpl'}} <p style="clear: both"><a href="javascript:" class="more-details">[...]</a></p> </div> </script> <script id="movieFullTmpl" type="text/x-jquery-tmpl"> <div class="movie-bag"> {{tmpl '#movieMainTmpl'}} <p style="clear: both"> :</p> <div> {{each frames}} <img src="Content/Frames/${$value}" /> {{/each}} </div> <p><a href="javascript:" class="more-details">[...]</a></p> </div> </script>
Here I use two templates, movieShortTmpl and movieFullTmpl , the common part of which is rendered into the movieMainTmpl template .Companion Code: $(function () { var shortTemplate = $('#movieShortTmpl').template('shortTemplate'); var fullTemplate = $('#movieFullTmpl').template(); $.tmpl('shortTemplate', dataItems).appendTo('#movieListBag'); $('#movieListBag').delegate('.more-details', 'click', function () { var item = $.tmplItem(this); item.tmpl = item.tmpl === shortTemplate ? fullTemplate : shortTemplate; item.update(); }); });
I think this code requires additional explanations.To replace the template, I need a link to the compiled template. I get these links using .template () calls . In addition, because
The shortTemplate template is used to render the movie list after the page loads, I save it in the cache so that I can instantiate it by name.The code of the handler for clicking the “More / less” links is very similar to the code from the previous example, in it I assign a link to the tmpl field to one of the previously compiled templates and call the update () method for rendering.In the browser, this example looks like this:Conclusion
The code of examples used in the article can be downloaded from this link .You can download jQuery Templates from the ASP.NET CDN website or directly from the GitHub repository:Documentation for jQuery Templates is available on the jQuery documentation site .In the examples, I used jQuery 1.5 RC1 , but jQuery Templates will work starting with jQuery 1.4.2 - in my last project, such a bundle was used.The links below will help you learn more about jQuery Templates :To get an idea of jQuery Templates performance compared to other “template engines”, take a look at the Brian Landau article “ Benchmarking Javascript Templating Libraries ”.And in conclusion, I want to thank Vitaly Dilmukhametov and Denis Gladkikh for the valuable comments made in the process of working on the article.