12

I came across the String interpolation feature in ES6. It works if I define the raw string in the code. But I want to read the raw string from a file and then replace the placeholders. How to do it?

file.txt

   hello ${customer.name} 

Node JS

   var customer = {name: 'sid'};
   var data = fs.readFileSync("file.txt","utf8");
   // what should go here so that data is 'Hello sid'?
sidgate
  • 14,650
  • 11
  • 68
  • 119
  • 4
    Nope, you need a proper template engine for that. – zerkms Sep 20 '16 at 01:08
  • @zerkms Incorrect. this is completely possible. – Daniel Jul 24 '21 at 21:14
  • @Daniel you have implemented a trivial template engine in your answer. The question was about **es2015 string interpolation**. – zerkms Jul 24 '21 at 22:08
  • @zerkms The question says: "...I want to read the raw string from a file and then replace the placeholders..." – Daniel Jul 24 '21 at 22:16
  • @Daniel yep, and then I said one needs a template engine, since there are no built-in facilities for that. And then you implemented a trivial template engine. – zerkms Jul 24 '21 at 22:24

5 Answers5

14

You can build a one-liner instead of loading a template engine. This will replace ${field} with values from an object.

var obj = {a: 'one', b: 'two'};
var templ = '${a}, ${b}';

templ.replace(/\${([^}]*)}/g, (r,k)=>obj[k]);

//  'one, two'
C B
  • 12,482
  • 5
  • 36
  • 48
  • This technically doesn't answer the question because the user wants to use nested objects... But really cool nonetheless – Daniel Jul 24 '21 at 20:47
5

I decided to go with es6-template-strings as it seems straight forward. Alternatively, handlebar is also good for complex expressions.Petr's suggestion to use eval also works but I have some constraints using eval for my project

var data = fs.readFileSync("file.txt","utf8");
var compiled = compile(data);
var content = resolveToString(compiled, customer);
sidgate
  • 14,650
  • 11
  • 68
  • 119
  • Yes, es6-template-strings is the way to go. At this point ES6 template syntax has no 'native' benefits, it is just another template engine with zero extra features, if any other template engine is used in the app (Handlebars, Nunjucks, EJS, etc), it is can be used here for consistency with the same degree of success. – Estus Flask Sep 20 '16 at 01:39
3

The only way is to escape the template string in the file and make an eval later, see this question for more details.

Community
  • 1
  • 1
Petr
  • 5,999
  • 2
  • 19
  • 25
0

Taking much inspiration from @John Williams' post, you can still do this in one line by adding a split and reducer to the regex-replace so that you can use nested objects.

var obj = {a: 'one', b:{b1:"Bravo-1",b2:'Bravo-2'},c:{c1:{c2:"Charlie-2"}}};
var templ = '${a}, ${b.b1}, ${b.b2}, ${c.c1.c2}';

console.log(templ.replace(/\${([^}]*)}/g, (r,k)=>k.split(".").reduce((acc,cur)=>acc[cur],obj)));
Daniel
  • 1,392
  • 1
  • 5
  • 16
0

Improving on @Daniel's solution, allowing for undefined variable references to be left alone. This allows multiple template interpolation passes with different variables matches.

// Interpolate template given parameter object.
// Can be chained, replacing only found variable paths.
// Strip whitespace from template variable reference.
// Will not work with ${"some var"."another var"}
const replace = function (templ, obj) {
    return templ.replace(/\${([^}]*)}/g, (r,k)=> {
        let ret;
        try { ret = k.replace(/ /g, '').split(".").reduce((acc,cur)=>acc[cur],obj); } catch (err) { }
        return (ret === undefined) ? r : ret;
    });
};
var obj = {a: 'one', b:{b1:"Bravo-1",b2:'Bravo-2'},c:{c1:{c2:"Charlie-2"}}};
var templMissing = '${ a}, ${b.b1 }, ${b.b2}, ${c.c1.c2}, ${bla} ${var.ble}';
console.log(replace(templMissing, obj));