Skip to content

Instantly share code, notes, and snippets.

@danielsokolowski
Last active August 29, 2015 14:01
Show Gist options
  • Save danielsokolowski/0954fc2a767f441720b9 to your computer and use it in GitHub Desktop.
Save danielsokolowski/0954fc2a767f441720b9 to your computer and use it in GitHub Desktop.
"use strict";
/*
* JQuery Substitute method allows for simple templating using JS Object dot notation.
* Contribute link: https://gist.github.com/danielsokolowski/0954fc2a767f441720b9
*
* @param strTemplate - string contain the replacement tokens
* @param objData - an Object represetnting your replacmenet values
*
* Example:
* var strTemplate = 'Hello {user.name}'
* var strDatra = {'user': 'Daniel Sokolowski'}
* alert($.substitute(strTemplate, objData)); // outputs 'Hello Daniel Sokolowski'
*
*/
$.substitute = function(strTemplate, objData) {
return strTemplate.replace(/\\?\{([^{}]+)\}/g, function(match, subMatch1) {
try {
if (match.charAt(0) == '\\')
return match.slice(1);
if (objData[subMatch1] != null)
return objData[subMatch1];
var keys = subMatch1.split(".");
var value = objData[keys.shift()]; // return first element and update the original array
while (keys.length !== 0) { // keep returning properties
value = value[keys.shift()]
}
return value != undefined ? value : match;
} catch (err) { // return any errors as a string
return match;
}
});
};
@DimitarChristoff
Copy link

@danielsokolwski

Done that before. what you have is not particularly great:

  • try/catch, slow.
  • shift, very slow.
  • your example references {user.name} where you don't have a name property
  • brute force walking until last one won't guarantee it will be the right match.
  • failing to find a matching key would return 'undefined' in your templates

try this instead

$.substitute = function(string, object, regexp){
    return String(string).replace(regexp || (/\\?\{([^{}]+)\}/g), function(match, name){
        if (match.charAt(0) == '\\') return match.slice(1);
        if (object[name] != null) return object[name];

        var path = name.split('.'),
            length = path.length,
            sub = object,
            i;

        if (length <= 1)
            return match;

        for (i = 0; i < length; i++){
            if((sub = sub[path[i]]) == null) return match;
        }
        return sub;
    });
};

var tpl = 'this user called {name} has {userDetails.posts.count} messages and {has} been a \\{name} since {userDetails.memberSince}';

var o = {
    name: 'Bob',
    userDetails: {
        memberSince: 'last Friday',
        posts: {
            count: 3
        }
    }
};

$('body').append($('<div></div>').html($.substitute(tpl, o))); 
// this user called Bob has 3 messages and {has} been a {name} since last Friday

with the above: http://jsfiddle.net/fDcF7/2/

your behaviour is different to mootools' in that {nomatch} would be replaced by an empty string whereas yours would return undefined - this user called Bob has 3 messages and undefined been a \Bob since last Friday

in the above code, non-matched tokens are left as-is so you can debug them easier. additionally, mootools has the ability to escape a token you don't want to serialize via \\.

despite of the extra checks in my code path for escape etc - http://jsperf.com/msub - it is still more performant than the try/catch one.

@danielsokolowski
Copy link
Author

@dimitarchristof - thank you for the feedback, the difference is about 6% faster good to know, I like returning the {...} for unmatched keys.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment