mesx
mesx

Reputation: 1165

Javascript String replacer for custom template

I would like to get some ideas how to achieve the following task. I'm writing a lightweight template language. That takes any array or json object and replaces the string-values with values from my local data store. Please let me illustrate how it works:

var obj = {
  prop: "_p{propnameA}",
  secondprop: "_p{propnameB}",
  thirdprop: "Hello, this is \"_p{propnameC}\" and _p{propnameD},
  arr: [
    "textvalue", "propB value = _p{propB}"
  ]
}

I've wrote an algorithm that iterates over each property of each json or array. Now i need a fast way to replace all my template-tags to their actual values. I would like to use different types of template-tags:

etc.

each template-tag means something different in my program. For Example: the template-tag _p{} calls a method in my application with the parameter of the tag-value. _p{propval} is a equivalent to myApp.getProperty("propval")

Other tags call other methods of my application. I am thinking about using a string.replace with a regular expression for my tags. But i run into two problems:

  1. How to write this regular expression?
  2. How to handle non-string return values?

the evaluated value of a tag must not always be a string. it could also be a more complex data type like an array or json object. In my first example code at the top of this question the resulting value for "_p{propnameA}" could be an array like [1,2,3,4]. Or _p{propnameB} could be a number and so my example on top should evaluate like:

obj = {
  prop: [1, 2, 3, 4],
  secondprop: 827,
  thirdprop: "Hello, this is \"valueC\" and valueD",
  arr: ["textvalue", "propE value = 827"]
}

obviously obj.secondprop should not have the string value "827" but the number instead while obj.arr[1] should be a string.

Do you got some smart ideas how to do this? Thank you very much for any help!

Upvotes: 0

Views: 104

Answers (2)

georg
georg

Reputation: 215029

If I understood correctly, you're looking for something like this:

// evaluate a single placeholder like _p{foo}

function evalPlaceholder(prefix, content) {

  switch(prefix) {
      case "_p": do_this();
      case "_c": do_that();      
      //etc
  }

}

// eval a string with placeholders

function evalTemplate(str) {

  var m = str.match(/^(_\w){([^{}]+)}$/);
  if(m) {
    // the whole string _is_ a placeholder
    return evalPlaceholder(m[1], m[2]);
  }
    
  // otherwise, the string can _contain_ placeholders
  return str.replace(/(_\w){(.+?)}/g, function(_, $1, $2) {
    return evalPlaceholder($1, $2);
  });

}

// walk an object recursively and eval all templates

function evalObject(obj) {
  
  Object.keys(obj).forEach(function(k) {
    var v = obj[k];
    
    if(typeof v == "object")
      evalObject(v)
    else
      obj[k] = evalTemplate(v);
    
  })

}

Upvotes: 1

Labib Ismaiel
Labib Ismaiel

Reputation: 1340

first, you can cast numeric values to strings in two ways, which I am sure you have seen something like this before:

1- toString function call: var x = 825; console.log(x.toString())

2- adding the number to a string: var x = '' + 825

so if you don't want numeric values, but only strings, just make sure you convert the value to string (even if it's a string nothing will happen) before you use it.

second, I don't think I really got your problem, but from what I got, your problem is much simpler that a regex, you're only replacing well defined string, so while iterating over the values all you need is:

var p = prop.toString();
if(p.startsWith("_p") {
  p.replace("_p", "_c)
}

I hope this is what you are looking for

Upvotes: 0

Related Questions