2

I'm working on a function to search through an objects properties, and replace 'handle bar' values in a string.

Question

How do I change the following code to replace {{phones.primary}} with its value.

function template(content, values) {

    for(prop in values) {

        content = content.replace('{{' + prop + '}}', values[prop]);

    }

    return content;
}


alert(template('Hello {{name}}. Your primary number is {{phones.primary}}', {

    name: 'Mickey Mouse',
    phones: {
        primary: '123-123-1234'
    }

}));
4
  • 2
    Reason you are not using a library that already does this? Ractivejs or handlebars? Commented Sep 23, 2015 at 16:01
  • 5
    Because sometimes it's good/fun to work things out for yourself rather than rely on a library. Commented Sep 23, 2015 at 16:04
  • This is the naive approach and has geometrically bad performance. The more values you have, the slower it gets. To fix this, try and compile one regular expression that matches in a general way. If you're doing this from an academic perspective, that's fine, try to state that so people can focus on helping you instead of steering you away from re-implementing the wheel. Commented Sep 23, 2015 at 16:07
  • 1
    Use regular expression replace. It allows you to provide a function as the replacement -- it calls the function with the original matched text, and the function returns the replacement. Commented Sep 23, 2015 at 16:14

3 Answers 3

5

Instead of looping through the object and checking whether that key/value pair exists in the string, use a regex to pull out all the {{name}} strings. Then you can use the callback to search the object for the value you need.

function template(content, values) {
    // This will search the string for `{{name}}`
    // The 1st param to the callback is the entire match (`{{name}}`)
    // and the 2nd is the first capture group (`name`).
    return content.replace(/{{(.+?)}}/g, function(match, prop){
        // Convert keys like `phones.primary` into an array of paths
        // then "reduce" that array into one value.
        // This lets us go through the object and drill down
        // until we get the value we want.
        return prop.split('.').reduce(function(obj, key){
            return obj[key];
        }, values);
    });
}
Sign up to request clarification or add additional context in comments.

Comments

3

Your issue occurs because you dont have a way of accessing a property in an object. you can use regex to match all the handlebars{{}} and then use another function to drill down if it is an object:

function evaluateProperty(values, path) {
    var parts = path.split(".");
    var result = values;

    parts.forEach(function(part) {
        result = result[part];
    });

    return result;
}

function template(content, values) {

    content = content.replace(/\{\{(.*?)\}\}/g, function(match, a) {
        return evaluateProperty(values, a);
    });

    return content;
}


alert(template('Hello {{name}}. Your primary number is {{phones.primary}}', {
    name: 'Mickey Mouse',
    phones: {
        primary: '123-123-1234'
    }
}));

Comments

0

I have something similar. It takes a templated string and returns a function. That function takes an object (including tree objects like yours) and returns that original string with the data populated in.

It uses underscore.

/*
      given a template string, returns a function that can be passed data and will return the populated string
     */
     function compile_template(str)
      {
          var replace_in = function(rep, pre, prop_name, val)
          {
              if(typeof val == "object")
              {
                  if(rep.search(new RegExp("\\{\\{" + pre + prop_name , "g")) >= 0)
                  {
                    _.each(val, function(v, k){ rep = replace_in(rep, pre + prop_name + ".", k, v); });
                  }
                  return rep;
              }
              else
              {
                  return rep.replace(new RegExp("\\{\\{" + pre + prop_name + "\\}\\}", "g"), val);
              }
          };
        return  function(obj)
                {
                  var representation = str;    
                  _.each(obj, function(v, k)
                  {
                      representation = replace_in(representation, "", k, v);
                  });
                  return clean_representation(representation);
                };
      }
     
     
     /*
       removes any unmatched mustache variables from the string.
     */
     function clean_representation(str)
     {
         return str.replace(/\{\{[^}]*\}\}/g, "");
     }

     //example:
     //var mytemplate_f = compile_template("Hello {{name}}. Your primary number is {{phones.primary}}");
     //console.log(mytemplate_f({name:"Mickey Mouse", phones:{primary:"555-5555"}})); => "Hello Mickey Mouse. Your primary number is 555-5555"
<script src="http://underscorejs.org/underscore-min.js"></script>

Comments

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.