Insert params in template

There is the good way to insert params in template with backtick operator…

let param = 'foo';
let message = `this is ${param}`;

But what to do, if message comes from server? I know a couple of variants, as replace placeholder or execute eval()… But probably someone has more ideas?..

The answer to this particular riddle all depends on the format of the message from the server. There are different techniques based on that.

To help avoid discussion being wasted, can you give us an example of the message from the server?

If you would to know, wether it is possible to compile whole message on server or on client?.. No it is not. Template should come from server due i18n, params exist on client only. So specific example has no meaning.

Incorrect. I was not asking anything about that, and don’t plan to either.

I think, I need something like that (tagged templates): Template literals (Template strings) - JavaScript | MDN (mozilla.org)

Now I have that…

function render(template, params)
{
    let result = params;

    template = 'result = `' + template + '`;';

    eval(template);

    return result;
}

console.log(render('this is the ${params.foo}', {foo: 123})); // this is the 123

In order to parameterize a template literal you can wrap it in a function:

function render ({ foo }) {
  return `this is ${foo}`
}

console.log(render({ foo: 123 }))

You can’t pass a template literal to the render function though, as the template will get evaluated immediately then; so if you have multiple templates just give each it’s own wrapper:

const renderThis = ({ foo }) => `this is ${foo}`
const renderThat = ({ bar }) => `that is ${bar}`

console.log(renderThis({ foo: 123 }))
console.log(renderThat({ bar: 42 }))

Problem is, that template could not be represented as constant. I have a variable template and I don’t know, how it looks like and how many params it has.

In that case template literals may indeed not be the right tool for the job – they’re called template literals after all. So rather than dangerously eval()uating random code to make them behave like something they’re not, you’re probably better off using (or writing) an actual template engine for this; this might be as simple as regular expression replacements:

function render (template, params) {
  return template.replace(/\${(.*?)}/g, (_, key) => params[key])
}

console.log(render(
  'hello ${foo} this is ${bar}',
  { foo: 'world', bar: 'a template' }
))
1 Like

Yes, I wrote it above, I have a choice between RegExp and eval(). I don’t like reg exps - they are querry of hard recognizable errors. But possible injection in eval() - this is serious argument. Though I think, templates will created by developer.

Well then, another possibility would be using template elements, and then use the DOM API to replace child elements:

<template id="my-template">
  Hello <span data-param="foo"></span>
  this is <span data-param="bar"></span>
  with some <span data-param="baz">defaults</span>
</template>
function render (templateId, params) {
  const template = document.getElementById(templateId)
  const fragment = template.content.cloneNode(true)

  fragment.querySelectorAll('[data-param]').forEach(element => {
    const replacement = params[element.dataset.param]

    if (replacement !== undefined) {
      element.replaceWith(replacement)
    }
  })

  return fragment.textContent
}

console.log(render('my-template', {
  foo: 'world',
  bar: 'a template'
}))

If the templates contain content dynamically generated on the server side, this would have the additional advantage that you don’t have to generate any JS but only actual markup.

That’s interesting, but not…

Look, I have just i18n messages, e.g…

English: This is some ${name}
German: Das ist eine ${name}

I woould to use it on client. So I send one of this messages (by current locale) to client with hidden and insert there param ‘name’.

You mean, I should to send not just hidden, but template-element that previously saved in i18n-file? Hm… I think, it’s too hard way.

Ah okay, yes for for such a case template elements would be overkill… in fact you could just do string replacements instead of using regular expressions here:

function render (template, params) {
  return Object.keys(params).reduce((result, key) => {
    return result.replaceAll('${' + key + '}', params[key])
  }, template)
}

console.log(render('hello ${name}!', { name: 'world' }))

But whatever you do, don’t use eval(). :-)

Yes, actually in this case reg exps not really required. I can to get placeholder with '{' + key + '}'. But placeholders of some specific format - it is also not really clean solution. I hoped, JS experts will find some another alternative. But if not, then I think, placeholders is the best that exists.