UPDATE: A joint announcement has just been made by jQuery and Microsoft that the jQuery Templates, Data Link and Globalization plugins are now 'Official jQuery Plugins'. In addition, a full set of documentation for the jQuery Templates and Data Link plugins is now available on the http://api.jquery.com/ site. See my next post: jQuery Templates is now an Official jQuery Plugin for details.
In my last post, I said I planned to start a series of posts introducing jQuery Templates. This is the first of that series. This post also introduces the Sample Viewer, which you can use to try out your own jQuery templates. In a sense jQuery Templates consists of three plugins: .tmpl(), .tmplItem() and .template(), and each one comes in two flavors: instance plugin and static plugin. From a basic scenario point of view, it is like this:
- .tmpl(): Render the template
- .tmplItem(): Find the template item
- .template(): Compile/Store the template
- ${...}: Evaluate fields or expression
- {{each ...}}...{{/each}}: Iterate without creating template items
- {{if ...}}...{{else ...}}...{{/if}}: Conditional sections
- {{html ...}}: Insert markup from data
- {{tmpl ...}}: Composition, as template items
- {{wrap ...}}...{{/wrap}}: Composition, plus incorporation of wrapped HTML
Rendering a template using local data within the page
Here is some data:var movies = [ { Name: "The Red Violin", ReleaseYear: "1998" }, { Name: "Eyes Wide Shut", ReleaseYear: "1999" }, { Name: "The Inheritance", ReleaseYear: "1976" } ];Here is some markup to be used as a template:
<script id="movieTemplate" type="text/x-jquery-tmpl"> <li> <b>${Name}</b> (${ReleaseYear}) </li> </script>and a target element where we are going to render the result of rendering the template against our data:
<ul id="results"></ul>And here is some code to take the template, render it with the data, and append the resulting HTML as content under our target element:
$( "#movieTemplate" ).tmpl( movies ) .appendTo( "#results" );In the above code, we get a jQuery wrapped set containing our template markup, and use the .tmpl() plugin method (to which we pass the data) to render the template. Then we chain with the appendTo method to append the results under our target element:
Demo:
As you see the template got rendered once for each item in the movies array. Data values are inserted using the template tag ${...}. In fact ${expression} inserts the (HTML-encoded) result of evaluating the expression term, in the context of the current item.
The template engine actually exposes the current data item as the variable $data, and also exposes each of the fields of the current data item as individual variables. So the most simplest use-case of ${...} is just with a field name as expression, such as ${ReleaseYear}. This is actually equivalent to ${$data.ReleaseYear} and inserts the value of that field on the current data item. Pretty straightforward.
Here is the complete code of our example:
<script src="http://code.jquery.com/jquery.js" type="text/javascript"></script> <script src="jquery.tmpl.js" type="text/javascript"></script> <script id="movieTemplate" type="text/x-jquery-tmpl"> <li> <b>${Name}</b> (${ReleaseYear}) </li> </script> <ul id="results"></ul> <script type="text/javascript"> var movies = [ { Name: "The Red Violin", ReleaseYear: "1998" }, { Name: "Eyes Wide Shut", ReleaseYear: "1999" }, { Name: "The Inheritance", ReleaseYear: "1976" } ]; $( "#movieTemplate" ).tmpl( movies ) .appendTo( "#results" ); </script>
Playing with the data and the template: Sample Viewer
To get the feeling of how it works, here is the same demo again, but this time I have used a special Sample Viewer script which is integrated into my blog, so that if you mouse over the demo, you will see a little '+' button that you can click on. The result will be a tabbed view in which you can see the data and the template, as well as the result:
Demo with Sample Viewer (Mouse over...)
Try mousing over the demo above, clicking on the '+' button, and modifying the data or the template under the respective tabs. Go back to the result tab and you will see how it renders. Click on the '-' button, and you will be back with the orginal data and template...
By the way, this sample viewer is actually implemented using jQuery templates, and illustrates the kind of dynamic interactive client-side UI that can be built very easily with jQuery templates. Maybe at some point I'll reach the point of blogging about how I went about building the sample viewer. But for now, let's get back to just playing with it. For example, if you want to just change the data, and see how the template rendering works with your changes, here are some examples of changes to the data that you could explore:
Changing the data
Mouse over the demo above and expand the Sample Viewer. Now try copying and pasting the data examples below into the Data tab, and then switching back to the Result tab. Change values, number of elements etc.[ { "Name": "The BLUE Violin", "ReleaseYear": "1998" } ]Conclusion: It actually is data-driven :-) Remove some fields and add others:
[ { "Name": "The Red Violin", "ReleaseYear": "1998" }, { "Name": "Eyes Wide Shut" }, { "Name": "The Inheritance", "ReleaseYear": "1976", "Director": "Mauro Bolognini" } ]Conclusion: It ignores the missing/undefined values without error. If you put the value of those fields to null or to the empty string, the result is the same. And of course the added fields have no effect, unless you want to add ${Director} to the template. (Try it...) Replace the array with a single object:
{ Name: "The Red Violin", ReleaseYear: "1998" }Conclusion: The templating engine is smart about arrays. Pass an object and it renders the template once, with the object as data item. Pass it an array and it creates a template item (a rendered template) for each of the data items in the array. Set the data to null:
nullConclusion: If you pass no data at all, the templating engine still renders the template once, but the current data item is null. We will see that there are many scenarios where you are just rendering boiler-plate, or where the template pulls in data from other places than the data item, so passing data to the template is not always appropriate or relevant. It may be a nested template, and use data from the parent item. It may have template tags whose parameters are not simple values, but function calls, and the template is driven by the data returned by that function call. For example it might include {{each myApp.getData(foo)}}, or ${myApp.getData(foo)}. In this blog I am staying with much simpler examples, but we will see in later blogs how passing functions to template tags is extremely common. Include some HTML markup in the data:
{ "Name": "The <strong style="color: Red;">Red</strong> Violin", "ReleaseYear": "1998" }Conclusion: This does not change the formatting of the text. Instead, it shows the markup in the rendered UI. This is by design: ${expression} HTML-encodes the value before inserting it into the DOM. If you actually want your markup to get inserted into the DOM, then use the {{html ...}} template tag rather than the ${...} template tag. You can try it now: go to the #movieTemplate tab and replace ${Name} by {{html Name}}. Now the markup will not be escaped, and the data will actually get inserted as HTML. (We'll come back to this lower down in the blog). One detail: the sample viewer is using JSON2 to convert between string expressions and JavaScript objects. So the text you are editing above is actually JSON, not JavaScript literals. That's why the keys are wrapped in quotes. Try removing them, and the sample viewer will tell you that you have a syntax error! But in your script, of course, you have literals, and those quotes can be omitted (as long as you avoid JavaScript keywords!)
Changing the template
Let's try changing the template, now, rather than the data. For convenience, here is the sample viewer again. This time I set it to show the tabs from the get-go, so you don't need to mouse over and click the '+' button... Sample Viewer<li> <b>${Name}</b> (Released in the year ${ReleaseYear}) </li>Conclusion: It works as you would expect! Add some markup and some formatting:
<li> <b>${Name}</b> (<span style="color: Blue;">${ReleaseYear}<span>) </li>Conclusion: It works as you would expect... Add simple JavaScript expressions:
<li> <b>${Name.toUpperCase()}</b> (${parseInt(ReleaseYear) + 100}) </li>Conclusion: This works too. You can put JavaScript expressions as parameters to the tags. But don't go overboard! There is not a complete JavaScript parser in the context of inline expressions in the template. If you have complex code, write a function, and call the function from your template, and pass parameters if you need to: ${myFunction(a,b)}. (More on that in a later blog...) Add another template tag:
<li> <b>${Name}</b> (${ReleaseYear}) - Director: ${Director} </li>Conclusion: Nothing yet - there is no Director field... Now change the data too:
[ { "Name": "The Red Violin", "ReleaseYear": "1998", "Director": "Francois Girard" }, { "Name": "Eyes Wide Shut", "ReleaseYear": "1999", "Director": "Stanley Kubrick" }, { "Name": "The Inheritance", "ReleaseYear": "1976", "Director": "Mauro Bolognini" } ]Conclusion: Yes, it works as expected! Finally let's make the change I mentioned in the previous section, and get a 'teaser' on one of the other template tags to be covered in later blogs: the {{html ...}} tag... First, include HTML markup in the data:
{ "Name": "The <strong style='color: Red;'>Red</strong> Violin", "ReleaseYear": "1998" }Now change the template to use the {{html ...}} template tag instead of the ${...} template tag:
<li> <b>{{html Name}}</b> (${ReleaseYear}) </li>Conclusion: Thanks to the {{html ...}} tag, you can insert HTML markup into the DOM, as HTML. Combining some of the changes above to data and template, here is a working example that illustrates what you did, and which you can use for exploring further changes: Sample Viewer
<script src="http://code.jquery.com/jquery.js" type="text/javascript"></script> <script src="jquery.tmpl.js" type="text/javascript"></script> <script id="movieTemplate" type="text/x-jquery-tmpl"> <li> <b>{{html Name}}</b> (<span style="color: Blue"> ${ReleaseYear}</span>) - Director: ${Director} </li> </script> <ul id="results"></ul> <script type="text/javascript"> var movies = [ { Name: "The <strong style='color: red'>Red</strong> Violin", ReleaseYear: "1998", Director: "Francois Girard" }, { Name: "Eyes Wide Shut", ReleaseYear: "1999", Director: "Stanley Kubrick" }, { Name: "The Inheritance", ReleaseYear: "1976", Director: "Mauro Bolognini" } ]; $( "#movieTemplate" ).tmpl( movies ) .appendTo( "#results" ); </script>