How do I compose templates on both server and client using dust.js

I'm trying to make a modern site that loads data using ajax, renders it in a template and uses jQuery and pushstate to display it, plus server-side rendering so that initial page loads are fast and spiders can crawl as intended. Of course, I read the article about linkedin using dust.js to achieve this.

Now, dust.js claims the following advantages:

  1. Composable: Designers should be able to break presentation markup into manageable components and combine these components at runtime. It should not be necessary to statically link templates or manually assemble 'layouts' inside application code.

  2. Format agnostic: While HTML generation and DOM manipulation are useful in specific instances, a general-purpose template system should not be tied to a particular output format.

So it sounds great, but how do I actually achieve what I want? I've made templates that render a complete page just fine on the server, but they use blocks and inline partials - which means the internal bits can't be rendered at all without the wrapper present (it just returns an error that says it can't find the wrapper template). I don't see how composing inside application code (as opposed to selling point #1 above) can be avoided on the client.

I don't understand what #2 above even means. I guess it means that you get the output as a string and can do whatever you want with it?

The documentation is about as clear as mud.

So what do I do? Is there a better option than dust.js these days? Do I write templates so that they must be composed in application code? If so, by what mechanism do I compose them in application code?

Ok, since there has been trouble understanding my question (which is itself understandable), I just threw together this (untested) example showing the problem:

Wrapper template:

<html>
    <head><title>{+title/}</title>
    {+styles/}
    </head>
    <body>
        header header header
        <div id="pagecontent">{+content/}</div>
        footer footer footer
        <script src="jquery"></script>
        <script src="dust"></script>
        <script src="see 'Client side script' below"></script>
    </body>
</html>

'time' template:

{>wrap/}
{<title}Time in millis{/title}
{<styles}
    <style>
    body {
        background-color: {bgcolor};
    }
    </style>
{/styles}
{<content}
    The time: {time}<br />
    <a href="{link}">Switch format</a>
{/content}

Server side code:

app.get('/millis',function(req,res) {
    res.render('time',{millis:new Date().getTime(),link:'/time',bgcolor:'lightgreen'});
}
app.get('/time',function(req,res) {
    res.render('time',{millis:new Date().toString(),link:'/millis',bgcolor:'lightpink'});
}

So, the server will render the page fine, but what about the client? Read on.

Client side script:

//load the 'time' template into dust somewhere up here
$(function(){
    $('a').click(function(e){
        var newcontent;
        switch($(this).attr('href')) {
            case '/time':
                //FAILS: can't find the wrapper. We need logic to get the title and styles from the template and fill it in in-place in the DOM
                newcontent = dust.render('time',{millis:new Date().toString(),link:'/millis',bgcolor:'lightpink'});
            case '/millis':
                //FAILS: can't find the wrapper. We need logic to get the title and styles from the template and fill it in in-place in the DOM
                newcontent = dust.render('time',{millis:new Date().getTime(),link:'/time',bgcolor:'lightgreen'});
            default: return;
        }
        e.preventDefault();
        $('#pagecontent').fadeOut(1000,function(){
            //use pushstate and stuff here
            $(this).html(newcontent);
            $(this.fadeIn(1000);
        });
    });
});

I was wondering the same thing and came across this. You may have seen this too, but I'm leaving this here in case it helps others.

http://spalatnik.com/blog/?p=54

I haven't implemented this so my deductions below are based off the above article and some (hopefully) educated assumptions. I'd certainly like to continue the discussion if the following is incorrect, as I'm learning as well.

I suspect that you'd have two types of wrapper templates. One is the wrapper template that you provided above (for server side rendering). A second wrapper template would be quite different (as you see below). I'm copying verbatim from the blog above in the following example. I presume that all your compiled DustJS templates are in the file dust-full-0.3.0-min.js below.

<html>
<head>
<script src="dust-full-0.3.0.min.js"></script>
<script type="text/javascript">
//example showing client-side compiling and rendering
var compiled = dust.compile("Hello {name}!", "index");
dust.loadSource(compiled);

dust.render("index", {name: "David"}, function(err, out) {
  if(err != null)
    alert("Error loading page");
  //assume we have jquery
  $("#pageContainer").html(out);
});
</script>
</head>

<body>
<div id="pageContainer"></div>
</body>
</html>

I suspect that in your Express server, you'd check the User Agent and decide on which template to render out. If it's a bot user-agent, use server-side generation in your example. Otherwise, use the client side template above.