简体   繁体   English

快速计算把手模板的价值?

[英]Calculate value for Handlebars template on-the-fly?

Is there a way to provide a function to Handlebars that calculates the value when needed, instead of supplying all values up-front in the context? 有没有一种方法可以为把手提供在需要时计算值的功能,而不是在上下文中预先提供所有值?

For example, say I wanted to fill out any template with example data: 例如,假设我想用示例数据填写任何模板:

var exampleFunction = function (varName) {
    return "Example " + varName;
};

If I know ahead of time what variables a Handlebars template needs, I could use this function to assemble a context. 如果我提前知道Handlebars模板需要哪些变量,则可以使用此函数来组装上下文。 However, what I really want to do is: 但是,我真正想做的是:

var template = Handlebars.compile(templateString);
var html = template.fillFromFunction(exampleFunction);

Is this possible? 这可能吗? If not, then are there any other template engines that support it? 如果没有,那么还有其他支持它的模板引擎吗?


Bonus question: can this be made asynchronous, eg: 额外的问题:这可以异步进行吗,例如:

var template = Handlebars.compile('{{foo.bar}}');
var dataFunction = function (path, callback) {
    setTimeout(function () {
        callback("Async " + path);
    }, 100);
};

Short version: 简洁版本:

It's a hack, but I've got a workaround. 这是一个hack,但是我有一个解决方法。

var oldNameLookup = handlebars.JavaScriptCompiler.prototype.nameLookup;
handlebars.JavaScriptCompiler.prototype.nameLookup = function (parent, name) {
    return '(typeof ' + parent + ' === "function" ? '
        + parent + '(' + JSON.stringify(name) + ') : '
        + oldNameLookup(parent, name) + ')';
}

Usage: 用法:

var template = handlebars.compile('{{foo}} {{bar}}');
var dataFunction = function (key) {
    return 'data:' + key;
};
console.log(template(dataFunction));
// Outputs: "data:foo data:bar"

Sub-properties: 子属性:

To enable sub-properties (eg "{{foo.bar}}" ), we need a wrapper method: 要启用子属性(例如"{{foo.bar}}" ),我们需要一个包装器方法:

function transformFunc(dataFunction) {
    return function (key) {
        if (typeof key !== 'string') {
            return dataFunction('');
        }
        var pointerKey = '/' + key.replace(/~/g, '~0').replace(/\//g, '~1');
        return transformFunc(function (path) {
            return dataFunction(pointerKey + path);
        });
    };
}

Usage: 用法:

var template = handlebars.compile('{{foo}} {{foo.bar}}');
var dataFunction = transformFunc(function (path) {
    // path is a JSON Pointer path
    return "data:" + path;
});
console.log(template(dataFunction));
// Outputs: "data:/foo data:/foo/bar"

If one wanted, I suppose .compile() and .precompile() could be modified so that transformFunc() was applied to any incoming function when templating. 如果有人.compile() ,我想可以修改.compile().precompile() ,以便在模板化时将transformFunc()应用于任何传入函数。

Explanation: 说明:

The hack includes altering the code-generation of Handlebars, but this code comment implies this is sort of OK. hack包括更改Handlebars的代码生成,但是此代码注释暗示这样做还可以。 I tried finding a way to subclass, but couldn't see how to get this or this to use it. 我试图找到一种方法,子类,却不见如何得到这个使用它。

Short version: override the nameLookup() method . 简短版本:覆盖nameLookup()方法

This method usually generates JavaScript code like depth0.foo or (depth1 && depth1.bar) . 此方法通常生成JavaScript代码,例如depth0.foo(depth1 && depth1.bar) We're extending it so the generated code first checks the parent to see if it's a function. 我们正在扩展它,以便生成的代码首先检查parent以查看它是否是一个函数。

If it's a function, then it calls the function with the property name as the argument. 如果是函数,则以属性名称作为参数调用该函数。 Otherwise, it returns the same value as previously. 否则,它将返回与以前相同的值。 For example, it will generate something like: 例如,它将生成如下内容:

(typeof depth0 === "function" ? depth0("foo") : (depth0 && depth0.foo))

For simple variables (eg just "{{foo}}" ) you can now just supply a function and it will be called with the variable names. 对于简单的变量(例如,只是"{{foo}}" ),您现在可以提供一个函数,并且将使用变量名来调用它。

Enabling sub-properties 启用子属性

However, for nested properties (ie "{{foo.bar.baz}}" ) we actually need our function to return another function, which can either return appropriate data or be called with another property name, depending which is needed. 但是,对于嵌套属性(即"{{foo.bar.baz}}" ),我们实际上需要我们的函数返回另一个函数,该函数可以返回适当的数据,也可以根据需要使用另一个属性名进行调用。

So: if our transformed function is not given a string, then it is assumed we are at the end-point (we want the actual data, not a sub-property), so we just call through. 因此:如果未向我们的转换函数提供字符串,则假定我们处于端点(我们需要实际数据,而不是子属性),因此我们直接调用。

If our transformed function is given a string, then it's assumed to be a key, so another (transformed) function is return that calls back to the data function, prefixing the argument appropriately. 如果给我们的转换函数一个字符串,则假定它是一个键,因此返回另一个(转换的)函数,该函数调用数据函数,并在参数前加上适当的前缀。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM