This is my Blog.

It's full of opinions on a wide range of stuff.

Using dust.js with Scala SBT

If you've been living under a rock for the past few weeks you might have missed LinkedIn bloging about their usage of dust.js. This idea of client-side templating is certainly a very interesting one, and something that is in all likelihood very useful for many large-scale applications currently in production. dust.js is quite nice in that it has no dependencies on any javascript library such as JQuery or YUI. This is really rather handy is it means no prescription on your style of implementation.

However, whilst the dust.js documentation has a wide range of examples demonstrating how to use the templating engine, what it does not have is any examples of how one might actually wrap it up and use it. So, with that I wanted to just write up a quick post to explain some of the things that might not be immediately obvious. Before getting to the meat of the article, it's worth bearing in mind some terminology:

  • Template: The source dust.js template

  • Compiled template: A pre-compiled version of the template; essentially some generated JavaScript that is created at build-time (it's also possible to create the JavaScript at runtime, but this is heavily discouraged)

  • Context: Each template can only be rendered with a context. More regularly we can think of this as the data that populates the template variables

In short, the developers writes a source template which is then compiled to JavaScript at build time and then rendered at runtime with a given context; which is likely a dynamic JSON payload.

When I first go going with dust.js I was unsure how to handle this build-time compilation. Frankly, the support for everything that isn't JavaScript or Node was (and is still mostly) non-existent. This was problematic as I certainly didn't want to be compiling templates on each and every request for a given HTML page. With that, I set about writing an SBT plugin for dust.js template compilation. This then allows you to have SBT build the dust.js templates into JavaScript that you can simply reference directly from your HTML (more on this later). The plugin itself takes templates it finds in src/main/dust and compiles them to the resource managed target whenever you invoke the dust task from the SBT shell. It's also super easy to redirect the compilation output to the webapp directory during development so that you can simply use the local Jetty server to tryout your app:

(resourceManaged in (Compile, DustKeys.dust)) <<= (
      sourceDirectory in Compile){
    _ / "webapp" / "js" / "view"
}  

But I digress. For more information about the plugin, see it's README file in the github repo.

After making my own tooling for SBT to work with dust.js, I then got to thinking about how one would actually wrap the dust.js rendering system. Curiously, the examples I found online suggest making an AJAX call to fetch the context, or template data. This strikes me as quite sub-optimal as it would mean creating the following requests:

  • fetching the general html for the page in the first instance
  • another request for the dust.js runtime
  • another request for the dust template
  • and then another for the context data

All that, just to render one template and add unnecessary lag to the page load whilst the ancillary request for context data takes place (after the initial page load has already completed!). For general page rendering, this seems somewhat bizarre. That is to say, requesting context data via AJAX for say, a single page application could well be a reasonable use-case, but for general pages that render "in full", it seems silly.

Assuming you're just rendering regular page content, we can make the separation between things one would want to load and cache locally in the browser, and things that need to be dynamic. For the most part, the context is the only thing you'd want to be dynamic and the more general plumbing code for rendering would probably be reusable throughout your rendering call sites. Consider a simple sample:

The dust template (lets call it home.dust in src/main/dust):

<div>
  <h2>Hey {name}</h2>
</div>  

This template will then be compiled to home.js and placed in the resource managed output location (as per your build config). The other thing to probably think about is if had several pre-compiled template files needed on a given page it would make sense to merge and minify them into a single JS file. Alternatively, if you were building a single-page application it might be nice to use a client-side loading framework like LABjs to pull in the template files lazily (i.e. as you needed them). Getting back tot eh example, and considering the aforementioned, lets assume a HTML page that loads the DOM skeleton and the static libraries needed for rendering:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
  "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
  <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
  <script type="text/javascript" src="js/dust.min.js"></script>
  <script type="text/javascript" src="js/view/home.js"></script>
</head>
<body>
  <h1>Home</h1>
</body>
</html>

Now, as it stands this page would display the heading "Home" and load the dust library along with the compiled template; otherwise it is essentially static. In order to render the dust template one would have to add something like the following to the page <head>:

<script type="text/javascript">
//<![CDATA[
dust.render("home", { name: "Fred" }, function(err, out) {
  document.getElementById('whatever').innerHTML = out;
});
//]]>
</script>

This simple bit of JS would render the content into the element with the "whatever" ID attribute. Now then, this will work, and prove the concept for sure. What it does not do however is provide a dynamic context, which almost certainly most users would want to do. In this way, I can see why people might see that it made sense to make an AJAX request to a pure JSON backend service to get the context dynamically, but as above, this would be a bit slow and cumbersome. Instead, I think it would probably make more sense to dynamically deliver a call to a rendering function with the desired context. Let's qualify that: when the page loads, the template has been loaded and so has the dust library, so all that is actually required is have a call site that starts the rendering. I think this need can quite nicely be served by a server-side helper function. In essence, when the page is loaded, the server generates a <script> tag. Here's an example:

<body>
  ...
  <script type="text/javascript" src="/view/home"></script>
</body>

Which in turn would deliver something like:

draw("home", { name: "Whatever" });

That is, where the draw function wraps the dust rendering system. This is just an example though as the point is that this could be any kind of JavaScript. For instance, it could be a backbone.js view or something along those lines: you deliver the rendering code along with the context. This at least seems more optimal than making a fuckton of JavaScript requests for context objects as other blogs suggest.

In the next post i'll be showing you how to combine all the latest hipster web programming tools like dust.js, CoffeeScript and Less with a Scala backend using Unfiltered to deliver the dynamic template contexts.

blog comments powered by Disqus