8

I am currently making a small module for NodeJs. For which I need a small help.

I will tell it like this. I have a variable with string. It contains a string html value. Now I need to replace $(title) something like this with my object { "title" : "my title" }. This can be expanded to anything with user provide. This is current code.I think that I need RegEx for do this. Can you guys help me with this?

var html = `<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Document $(title)</title>
</head>
<body>

  <h1>Test file, $(text)</h1>

</body>
</html>`;

function replacer(html, replace) {
  // i need a regex to replace these data
  //return replacedData;
}

replacer(html, { "title" : "my title", "text" : "text is this" });

1
  • Since you are using node, use one of the many competent templating engines out there, such as ejs. Commented Jan 11, 2016 at 17:06

5 Answers 5

11

You can use a simple template function using regex,

var replacer = function(tpl, data) {
  var re = /\$\(([^\)]+)?\)/g, match;
  while(match = re.exec(tpl)) {
    tpl = tpl.replace(match[0], data[match[1]])
    re.lastIndex = 0;
  }
  return tpl;
}

use like

var result = replacer(html, { "title" : "my title", "text" : "text is this" });

jsfiddle

detail here

EDIT

Actually as torazaburo mentioned in the comment, it can be refactored as

var replacer = function(tpl, data) {
    return tpl.replace(/\$\(([^\)]+)?\)/g, function($1, $2) { return data[$2]; });
}

jsfiddle

hope this helps

Sign up to request clarification or add additional context in comments.

4 Comments

Well, sure, but this won't work with things like $(title.toUpperCase()).
You could skip the loop and just say return tpl.replace(/\$\(([^\)]+)?\)/g, function(_, match) { return data[match]; });.
@torazaburo, OP did not asked for a complex replacer and most of the time the simple one is needed. And rightly so i have refactored the replace function
I just tried it in Chrome's console, it's not replacing anything in my string.
8

This solution uses template strings to do everything you want.

This solution has the advantage that, in contrast to the naive roll-your-own regexp-based template replacement strategy as proposed in another answer, it supports arbitrary calculations, as in

replacer("My name is ${name.toUpperCase()}", {name: "Bob"});

In this version of replacer, we use new Function to create a function which takes the object properties as parameters, and returns the template passed in evaluated as a template string. Then we invoke that function with the values of the object properties.

function replacer(template, obj) {
  var keys = Object.keys(obj);
  var func = Function(...keys, "return `" + template + "`;");

  return func(...keys.map(k => obj[k]));
}

We define the template using ${} for substitutions (instead of $()), but escaping as \${ to prevent evaluation. (We could also just specify it as a regular string literal, but would then lose multi-line capability).

var html = `<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Document \${title}</title>  <!-- escape $ -->
</head>
<body>

  <h1>Test file, \${text}</h1>       <!-- escape $ -->

</body>
</html>`;

Now things work exactly as you want:

replacer(html, { "title" : "my title", "text" : "text is this" });

Simple example:

> replacer("My name is ${name}", {name: "Bob"})
< "My name is Bob"

Here's an example of calculated fields:

> replacer("My name is ${name.toUpperCase()}", {name: "Bob"})
< "My name is BOB"

or even

> replacer("My name is ${last ? lastName : firstName}", 
    {lastName: "Jones", firstName: "Bob", last: true})
< "My name is Jones"

2 Comments

Any way to get that replacer to work when the template string references a key that does not exist in "obj"?
"eval" can be dangerous
1

Since you are using ES6 template string you can use a feature called 'tagged template strings'. Using tagged template strings you are allowed to modify the output of a template string. You create tagged template string by putting a 'tag' in front of the template string, the 'tag' is a reference to a method that will receive the string parts in a list as the first argument and the interpolation values as remaining arguments. The MDN page on template strings already provides an example template string 'tag' that we can use:

function template(strings, ...keys) {
  return (function(...values) {
    var dict = values[values.length - 1] || {};
    var result = [strings[0]];
    keys.forEach(function(key, i) {
      var value = Number.isInteger(key) ? values[key] : dict[key];
      result.push(value, strings[i + 1]);
    });
    return result.join('');
 });
}

You use the 'tag' by calling:

var tagged = template`<!DOCTYPE html>
<html lang="en">
<head>
   <meta charset="UTF-8">
  <title>Document ${'title'}</title>
</head>
<body>

  <h1>Test file, ${'text'}</h1>

</body>
</html>`;

Notice that interpolation of variables uses the syntax ${'key'} instead of $(key). You can now call the produced function to get the desired result:

tagged({ "title" : "my title", "text" : "text is this" });

Run the code example on es6console

3 Comments

i use NodeJs Streams to read the html file. can i use it with this?
NodeJS 5.0 and 4.0 supports template strings (when you start node with --harmony) , but you can not transform input streams to template strings. They have to be constructed by the ` ticks
I don't think you need the --harmony flag for template strings.
1

I used prototype. it looks cleaner.

String.prototype.replaceByObject = function(obj) {
    return this.replace(RegExp(Object.keys(obj).join('|'), 'g'), function(val) {
        return obj[val];
    });
}

use it like this

html.replaceByObject({
    "title" : "my title",
    "text" : "text is this"
});

Comments

0
var binddingData=function(id,data){
    var me=this,
        arr=[];
    arr=getControlBindding(id);
    arr.forEach(function(node){
        var content=getBinddingContent(node.getAttribute('DataTemp'),data);
        binddingToHtml(node,content);
    })

}
var getControlBindding=function(id){
    var me=this;
    return document.querySelectorAll('[DataTemp]');
}


var getBinddingContent=function(temp,data){
    var me=this,
        res='',
        hasNull=false;
    if(temp==null||typeof temp=='undefined'){
            return res;
        }
    res= temp.replace(/\$\{([^\}]+)?\}/g, function($1, $2) { 
        if(data[$2]==null||typeof data[$2]=='undefined'){
            hasNull=true;
        }
        return data[$2]; 
    });
    return hasNull?'':res;

}

var binddingToHtml=function(node,content){
    var me=this;
    if(node.getAttribute('IsDateTime')){
        node.innerText='';//if u want change it to datetime string
        return;
    }
    if(node.getAttribute('AddBr') && content==''){
        node.innerText='';
        var brTag=document.createElement('br');
        node.appendChild(brTag);
        return;
    }
   node.innerText=content;
}

You use the 'tag' by calling:

<div DataTemp="${d1}"></div> 
    <div DataTemp="${d2}"></div>
    <div DataTemp="${d3}"></div> 
    <div DataTemp="${d3}+ +${d1}"></div> 
    <div DataTemp="${d3}/${d1}"></div>     
    <div DataTemp="${d4}\${d1}"></div>     
    <div DataTemp="(${d5}\${d1})"></div> 
    <div DataTemp="(${d3}\${d1})"></div>

with data var data={d1:'t1',d2:'t2',d3:'t3'}

1 Comment

Welcome to SO. To make your answer helpful, please give a description at first and then post your code for solving the problem.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.