Object JavaScript – Using Infuser to Asynchronously Load Your Templates

imageIn Introduction to Templates in MVVM Using Knockout.js, Mustache, you learned how you can use templates to a display and interact with Knockoutjs. But what if you would like to reuse those templates? Would you like to be able to load the templates asynchronously? And would you like to use the same techniques to load templates that could be using in Knockout, underscore and jquery-tmpl?

Jim Cowart wrote infuser to provide a “generic-ized” utility that could interface with a given template engine and handle the fetching of templates from a remote resource.

This means you can put your template content in a folder so you can reuse it in multiple places. If your template engine expects your templates to be in SCRIPT tags, you don’t have to lose syntax highlighting, etc. in your IDE – you can still place them in their own files with a valid markup extension .

What Infuser Does

Jim explains that Infuser:

  • Asynchronously (or synchronously, if you must) retrieves templates and stores them locally in the client once retrieved.  Default storage options are in-memory (hash) or in SCRIPT tags (since some engines prefer that). You can write a different storage provider if necessary.
  • Provides hooks for a callback to be invoked after you “get” a template, OR if you use the “infuse” method, you have more extensive control about how the template is rendered (if it’s data-driven) and attached to the DOM – including pre- and post-attach options and a render override.
  • Provides a hook for telling infuser how your preferred template engine handles binding a model to a template (making it possible for infuser to work with several major template engines).

Infuser takes advantage of it’s own in-memory storage (once a template has been retrieved, it’s cached), and your browser’s cache is also leveraged if it has not been disabled.

But mostly, it abstracts away infrastructure/ceremonial code involved in retrieving, binding and rendering templates to the DOM.

Getting Started with Infuser

You can get Infuser from either GitHub or NuGet. Infuser relies on TrafficCop,which prevents duplicate AJAX requests for the same resource while an identical request is already running.

Both versions include TrafficCop.

Infuser on GitHub

You can get Infuser from GitHub

You then add infuser to the page where you want to use it. Such as:


<script type="text/javascript" src="ext/jquery-1.7.1.js"></script>
<script type="text/javascript" src="lib/combined/infuser_all.js"></script>
<script type="text/javascript" src="main.js"></script>

Infuser on GitHub provides code in lib/combined/infuser_all.js that includes TrafficCop.

The GitHub version includes many examples, including how Infuser can work with jQuery Templates and underscore.

Infuser from NuGet

Or you can load it into your Visual Studio project using NuGet.

image

Infuser relies on TrafficCop, so you will need to include TrafficCop in your scripts.


<script type="text/javascript" src="Scripts/jquery-2.0.0.js"></script>
<script type="text/javascript" src="Scripts/TrafficCop.js"></script>
<script type="text/javascript" src="Scripts/infuser.js"></script>
<script type="text/javascript" src="main.js"></script>

Using Infuser

Infuser provides two main ways to interact with templates.

  • Get. You provide a template name and a callback. You get the retrieved template as the argument to the callback.)
  • Infuse. You get some additional functionality.

Get Template Using Infuser

Let’s assume we have a static template that loads when a button is clicked. In the following example, Infuser looks for templates in a Templates directory (relative to the current document) that with a file extension of  .html. In this case, Templates/Hello.html.

Then, bind the #btnTemplate click event to a function that gets the Hello template. In our callback, we’re hiding the original content and removing it, then fading in the new content.

First the template file:


<h1>Hello Infuser</h1>
<p>This is my first simple template</p>

Infuse the template

Then infuse the template. Here is an example file:


<html>
<head>
<title>Basic Example</title>
</head>
<body>
<p>Try this in Chrome, while watching requests on the "Network" tab of the developer tools.</p>
<div>
<input id="btnTemplate" type="button" value="Say Hello!">
<span id="msg"></span>
</div>
<div id="target"></div>
<script type="text/javascript" src="Scripts/jquery-2.0.0.js"></script>
<script type="text/javascript" src="Scripts/TrafficCop.js"></script>
<script type="text/javascript" src="Scripts/infuser.js"></script>
<script>
var toggled = false;
$(function () {
$('#btnTemplate').click(function () {
infuser.defaults.templateUrl = "./Templates",
infuser.get("Hello", function (template) {
var target = $("#target");
target.hide().children().remove();
target.append($(template)).fadeIn();
if (!toggled) {
$("#msg").text("Next click uses a locally cached template");
}
})
});
});
</script>
</body>
</html>

The previous example shows how you use infuser.get to find a template file named Hello in the Templates directory and then receive that template in a callback. You can then use template to append it to the div element whose identity is target. Note that the second time the template is needed, Infuser automatically uses a locally cached template.

More Options Using Infuse

You can often have more than a single template on a page. What if you always wanted to take the approach of hiding, then removing the target content, and then fading in the new template? Writing that each time would be overkill.  That’s where “infuse()” comes into play.

Infuse takes two arguments: the template id or name and an options hash. There are lots of options that each have basic defaults. You only have to provide what you’re overriding.

  • preRender: a method with a signature of (target, template), where “target” is a selector used to target n-number of DOM elements where the template is to be rendered, and “template” is the retrieved template content prior to any “binding” (if a template engine is involved).
  • render: method with a signature of (target, template), where “target” is a selector used to target n-number of DOM elements where the template is to be rendered, and “template” is the completed template (i.e.- if you’re using a template engine, this is after the model has been bound to it).
  • postRender: method with a signature of (target), where “target” is a selector used to target n-number of DOM elements where the template has been rendered.
  • target: can be a function or a string. If it’s a function, it takes the template id as its only argument and should return – based on whatever transformation logic you desire – a selector (string).  (The default implementation is a function that takes the template id and returns it prefixed with “#”, making it a selector that targets a DOM element with an id matching the template name.) If it’s not a function, then the value provided is treated as a selector. Typical usage is to define a default function used for most cases, and then override it with a specific value in the options hash when needed.
  • loadingTemplate: an object containing a content member (the HTML to display while loading a template), plus transitionIn and transitionOut methods that can be used to handle the appearance of the loading template (i.e.- you can fade it in and fade it out, etc.).
  • bindingInstruction: a method with a signature of (template, model) where “template” is the content retrieved from the server and model is a JavaScript object that will be bound to the template.  For example, to tell infuser you are using jQuery templates, then you’d do this: infuser.defaults.bindingInstruction = function(template, model) { return $.tmpl(template, model); };.  It defaults to simply return the template content.
  • useLoadingTemplate: a boolean indicating if the loadingTemplate member should be used when loading templates.

Loading Your Page Using Infuse

Let’s start with an example of doing a page load spinning gif with infuse method to demonstrate some of the options.

Let’s take the previous example. Let’s look into the infuser.infuse parameters.

  • target. Instead of using jQuery to define the target, you can specify a selector for the DOM element o render the template. In this case, we use #target.
  • render. This is a function that receives the target and the completed template. You will see how we bind a Knockout template in a following section.
  • postRender. This is a function that receives the target selector for what should be done following rendering.

And it adds a to the defaults. It’s important to note that the defaults set on infuser.defaults will now apply to any template rendering on the page

  • infuser.defaults.loadingTemplate.content that includes the spinning icon

Sample Code

Here’s the sample code that uses the file ./templates/Hello.html from the previous example. It also loads and displays ./images/ajax-loader.gif while the template is being retrieved.


<!DOCTYPE html>
<html>
<head>
<title>Infuser Page Load</title>
</head>
<body>
<div>
<input id="btnTemplate" type="button" value="Say Hello!">
<span id="msg"></span>
</div>
<div id="target"></div>
<script type="text/javascript" src="Scripts/jquery-2.0.0.js"></script>
<script type="text/javascript" src="Scripts/TrafficCop.js"></script>
<script type="text/javascript" src="Scripts/infuser.js"></script>
<script>
var toggled = false;
$(function () {
// pre-loading image for our custom "loading template"
var loadingImg = $('<img />').attr('src', './images/ajax-loader.gif'),
origRender = infuser.defaults.render;
$('#btnTemplate').click(function () {
infuser.defaults.templateUrl = "./templates";
infuser.defaults.loadingTemplate.content =
"<div class='infuser-loading'>" +
"<img src='./images/ajax-loader.gif'>Loading…</div>"
infuser.defaults.loadingTemplate.transitionOut = function () { };
infuser.infuse("Hello", {
target: "#target",
render: function (target, template) {
// really ugly way to simulate long running template retrieval
setTimeout(function () {
origRender(target, template);
}, 4000);
},
postRender: function (target) {
if (!toggled) {
$("#msg").text("Click uses a locally cached template");
}
}
});
});
});
</script>
</body>
</html>

Now following the click, you will see this:

image

Infuser with jQuery Templates and Underscore

You can find examples of how to use Infuser with jQuery Templates and Underscore in the Infuser example folder on GitHub.

Reference

Infuser – a Template Loader