tl;dr: Is it possible to make a reusable template literal?
I've been trying to use template literals but I guess I just don't get it and now I'm getting frustrated. I mean, I think I get it, but "it" shouldn't be how it works, or how it should get. It should get differently.
All the examples I see (even tagged templates) require that the "substitutions" be done at declaration time and not run time, which seems utterly useless to me for a template. Maybe I'm crazy, but a "template" to me is a document that contains tokens which get substituted when you use it, not when you create it, otherwise it's just a document (i.e., a string). A template is stored with the tokens as tokens & those tokens are evaluated when you...evaluate it.
Everyone cites a horrible example similar to:
var a = 'asd';
return `Worthless ${a}!`
That's nice, but if I already know a
, I would just return 'Worthless asd'
or return 'Worthless '+a
. What's the point? Seriously. Okay the point is laziness; fewer pluses, more readability. Great. But that's not a template! Not IMHO. And MHO is all that matters! The problem, IMHO, is that the template is evaluated when it's declared, so, if you do, IMHO:
var tpl = `My ${expletive} template`;
function go() { return tpl; }
go(); // SPACE-TIME ENDS!
Since expletive
isn't declared, it outputs something like My undefined template
. Super. Actually, in Chrome at least, I can't even declare the template; it throws an error because expletive
is not defined. What I need is to be able to do the substitution after declaring the template:
var tpl = `My ${expletive} template`;
function go() { return tpl; }
var expletive = 'great';
go(); // My great template
However I don't see how this is possible, since these aren't really templates. Even when you say I should use tags, nope, they don't work:
> explete = function(a,b) { console.log(a); console.log(b); }
< function (a,b) { console.log(a); console.log(b); }
> var tpl = explete`My ${expletive} template`
< VM2323:2 Uncaught ReferenceError: expletive is not defined...
This all has led me to believe that template literals are horribly misnamed and should be called what they really are: heredocs. I guess the "literal" part should have tipped me off (as in, immutable)?
Am I missing something? Is there a (good) way to make a reusable template literal?
I give you, reusable template literals:
> function out(t) { console.log(eval(t)); }
var template = `\`This is
my \${expletive} reusable
template!\``;
out(template);
var expletive = 'curious';
out(template);
var expletive = 'AMAZING';
out(template);
< This is
my undefined reusable
template!
This is
my curious reusable
template!
This is
my AMAZING reusable
template!
And here is a naive "helper" function...
function t(t) { return '`'+t.replace('{','${')+'`'; }
var template = t(`This is
my {expletive} reusable
template!`);
...to make it "better".
I'm inclined to call them template guterals because of the area from which they produce twisty feelings.
To make these literals work like other template engines there needs to be an intermediary form.
The best way to do this is to use the Function
constructor.
const templateString = "Hello ${this.name}!";
const templateVars = {
name: "world"
}
const fillTemplate = function(templateString, templateVars){
return new Function("return `"+templateString +"`;").call(templateVars);
}
console.log(fillTemplate(templateString, templateVars));
As with other template engines you can get that string from other places like a file.
There can be issues using this method like template tags are hard to use, but those can be added if you're clever. You also can't have inline JavaScript logic because of the late interpolation. This can also be remedied with some thought.