Search code examples
javascriptnode.jsdust.js

Async self-closing helpers with Dust.js


I've been trying out Dust.js recently because it supports asynchronous helpers but I've found a case where this does not seem to be so.

For example, given the following Dust template:

<ul>
  {#getResults}
    <li>{#isResultNew"}*New*{/isResultNew} {message}, {#formatResultDate format="d/m/y" /}</li>
  {/getResults}
</ul>

If the formatResultDate helper is synchronous then it's no problem, I can write out the date:

function formatResultDate(chunk, context, bodies, params) {
  ...
  return chunk.write(value)
}

However, if the template were asynchronous - using a promise - then nothing will be output:

function formatResultDate(chunk, context, bodies, params) {
  ...
  return Promise.resolve(date).then(value => chunk.write(value))
}

I can work around this limitation by supplying a tag body ({#formatResultDate format="d/m/y"}{.}{/formatResultDate}) but it's not quite the behaviour I'd expect.

I'm aware that async self-closing tags may not be possible; delving into the source code I found this comment but I don't really understand what it means and I haven't yet found any further explanation in the documentation or searching.


Solution

  • Just use {formatResultDate} to execute the helper as a reference-- a self-closing helper has no body to render.

    Additionally, a helper should either return a value or return the modified chunk-- it shouldn't both write to a chunk and then return some non-chunk value.

    Just return the promise itself and Dust will do the rest.


    Update based on your comment:

    There are two types of helpers in Dust. If you include your helper function as part of your context it is a "context helper". If you attach it to the dust.helpers object it is a "global helper".

    The comment you linked (which I wrote) indicates that global helpers can self-close. {@formatResultDate/} will output its return value into the template. Context helpers, however, can be accessed either as references or as block sections (although as you mentioned, you cannot pass parameters to a reference). Because Dust does not try to render sections with no body, context helpers follow those semantics.

    Since you need to pass parameters, you have a couple options.

    1) You can attach your helper to the dust.helpers object and access it like {@formatResultDate date=myDate /}. The helper can return a Promise and the value of that Promise will be output into the template. More info: Dust Helpers (see the Adding New Helpers section)

    2) You can handle Promises the old-school way, just like everyone did before Dust 2.7, by using chunk.map. This function signals to Dust that your chunk is asynchronous. To do this, look at the section of the Context Helpers guide named "Asynchronous context helpers". You would write something like this:

    "formatResultDate": function(chunk, context, bodies, params) {
      return chunk.map((chunk) => Promise.resolve(date).then(chunk.end));
    }