Monday, December 19, 2011

Dust JS templates as a view option for Spring MVC

You did what?

I thought it would be fun to integrate Dust as a templating option for Spring MVC applications. It was. So, the other day I was reading an interesting article by LinkedIn engineer Veena Basavaraj about their decision to move toward using client-side templates to help with reuse and unification of their disparate UI technologies (Grails, some Java framework, Sinatra on JRuby). After evaluating a bunch of client-side templating technologies, then ended up deciding on Dust. It's unique value proposition is it's ability to stream template processing in addition to some of the same features provided by mustacheor handlebars. The impetus for this experiment, however, was this line in the article:
Server side support: if you have a client that can't execute JavaScript, such as a search engine crawler, a page must be rendered server side. Once written, the same dust.js template can be rendered not only in the browser, but also on the server using node.js or Rhino.
Now, that's an interesting concept.  I'm not a stranger to passing JSON and a template to render views on the client, but if I ever thought of rendering the same template both on the client and server outside of the context of node.js I must have dismissed it. After thinking about it again I set out to run Dust on the server.

In essence, I wanted to be able to have a Spring MVC controller like so:


And a Dust JS template like so:


And have a user hit the application with the path "/home/Arianta" to render the template client-side and display:
Hello, Arianta!
Are you doing it fer giggles?
So, that is what I did.

Ok, how did you do that?

You can check out the code on GitHub, play with it, run it, throw it in the recycle bin, whatever.  The first thing I did was to create a basic Spring MVC application from a maven archetype.  Spring MVC defers to ViewResolvers to resolve and render views and provides some default implementations for some templating technologies such as Velocity and Freemarker.  Which was really nice to have some source code to look at when trying to create my own.  As it turns out, the ViewResolver can be pretty simple:


The only thing it really does is tell Spring what view class to use for views resolved with the view resolver.  The view class is where the meat of the work happens:


The code is pretty straight forward and should be self explanatory for most things.
  • In lines 8-16 we're just setting up Rhino.
  • Lines 17-18 expose the absolute path of the application root and the model from the Spring MVC controller to our javascript code.
  • Line 19 grabs the template based on the "url()" provided by Spring, based on the ViewResolver configuration that we'll see later, and passes it as a string to the javascript.
  • Line 21 executes the bootstrapping code needed to run the dust template compiler and processer.
  • Line 23 prints out the result of the template processing to the response.  The result variable you see there is actually a globally scoped variable from the bootstrap javascript code that gets the processed template assigned to it.
The code that is executed on line 21 of the DustView class is the javascript code that will take the model from the controller and the resolved template and process them using dust.  That code is pretty simple as well and looks like this:



Of note:
  • Line 1 is the global result object which allows our java code to pluck out the processed template and write it to the response
  • Line 4 is where we load Dust.  We have to load Dust full because we will be compiling the templates.  This should have been so simple but it wasn't.  Dust depends on the window variable outside of a CommonJS environment like node.  So I looked into bringing in Env.js to simulate a browser environment and just kept running into one problem after another.  Then I realized that it wasn't really using any browser functionality but just needed a "window" object.  That was a "duh" moment.  So I just modified the Dust source code to have a window object and moved on :) What a time suck that was...
  • Lines 11-15 just convert the model from a Java Map to a JavaScript Object.  This is just a shallow conversion and as such isn't very useful for more than playing with.
  • Line 17 compiles the template that was resolved by spring.  We'll see what that template looks like in just a bit
  • Line 19 just loads the compiled script into Dust
  • Line 20 renders the template based using the model for interpolated values.  This is exactly what would happen in the browser were we to just pass a template and some JSON to the client.
  • Line 21 assigns the output of the render to the global result variable.
The code is really straight forward.  The thing that amazed me and I haven't really looked into why, is that this code is being run synchronously on the Java side, but as you can see the template gets processed asynchronously.  So, even though it runs async it must finish before the value is picked up on the Java side of things.  Not that it's a big deal, but another reason this is just for fun.

The one missing piece that I haven't mentioned is how to configure this in Spring.  You actually do that the same way you would configure FreeMarker, except you define it with the new DustViewResolver.  So it would look like this:


That's really all there is to it.  So, it's possible and with very little code as well.  Now it just needs caching support, partial support, streaming support... Have fun with that folks.  Also, if you expand on this idea at all, drop me a line I'd love to hear about it.