I am trying to write an adapter for a client-side HTML/JS templating system to use dust.js under the hood. Unfortunately the API expects render operations to occur synchronously: the rendered output should be returned from the render() call. Dust.js is asynchronous and passes render output to a callback function. Is there any way to work around this, either in the Dust APIs or through some crazy Javascript hack?
DustJS is only going to execute things asynchronously when the resources it needs to render (templates, partials) haven't already all been loaded.
If all the dependencies of a template are loaded before you execute that template then it'll execute synchronously (as far as I can tell anyhow). So you can do something like:
var result;
dust.render("tpl", data, function(err, res) {
result = res;
});
console.log(result); // result will actually already be filled out if dustjs didn't
// have to go look for resources somewhere.
Here is a fuller example below: (and here is a jsfiddle link so you can run it: http://jsfiddle.net/uzTrv/1/)
<script type="text/javascript" src="dust.js"></script>
<script>
var tpl = dust.compile("Omg {#people} {.} {/people} are here! {>partial/}", "tpl");
var partial = dust.compile("I'm a partial but I've already been included so things still run {how}", "partial");
dust.loadSource(tpl);
dust.loadSource(partial);
var data = {
people: ["jim", "jane", "jack", "julie"],
how: "synchronously!"
};
var result;
dust.render("tpl", data, function(err, res) {
result = res;
});
console.log(result);
</script>
There could be cases (besides the one I mentioned) where I'm wrong... I don't know everything about dustjs.
I too wanted to have a function that accepted a context and returned the dust rendered text. Here is the solution I came up with:
// This function sets up dust template, and returns a new function "dusterFn()"
// dusterFn() can be passed a Context, and will return the rendered text.
// @param {String} text: The template text.
// @param {String} [name]: The name of the template to register with dust. If none is provided, a random number is used.
// @param {Function} [onError]: A function that is called if an error occurs during rendering.
function getDusterFn(text, name, onError) {
var dusterFn = null;
name = name || Math.floor(Math.random() * 99999).toString();
onError = onError || function (error) { };
try {
var compiled = dust.compile(text, name)
dust.loadSource(compiled);
dusterFn = function (context) {
var dustOutput = '';
dust.render(name, context, function (error, out) {
if (error) onError(error);
dustOutput = out;
});
return dustOutput;
};
} catch (e) {
// invalid template syntax
e += "\n\nPlease check your template syntax.";
throw (e);
}
return dusterFn;
}
Usage
var greetingTemplate = getDusterFn('Hello {name}, You are {age} years old!');
greetingTemplate({name: 'Jane', age: 24});
Matt's solution gave me some pointers on how to write a little wrapper that hides the "ugliness" of his solution (by "ugliness" I mean declaring variable outside of callback, assigning value inside callback and returning outside callback).
It not only wraps the hack into a little function but also binds the template´s name. I find this incredible helpful as I find myself using the same render function over and over again, but I do not want to specifiy the template´s name every time.
function templates(template) {
return function templatesWrapper(data) {
var result;
dust.render(template, data, function onRender(err, data) {
if (err) {
throw err;
}
result = data;
});
return result;
}
}
This is how to use it:
var renderHello = templates('hello.html');
renderHello({ username: 'Stackoverflow' });
// => <h1>Hello, Stackoverflow</h1>
© 2022 - 2024 — McMap. All rights reserved.