Custom Tags
你可以添加更多的自定义扩展,nunjucks 提供了 parser api 可以对模板做更多的事。
注意:当预编译时,你必须在编译时添加这些扩展,你应该使用 precompiling API (或者 grunt 或gulp任务),而不是预编译脚本。你需要创建一个 Environment
实例,添加扩展,传到预编译器中。
一个扩展至少有两个字段 tags
和 parse
,扩展注册一个标签名,如果运行到这个标签则调用 parse。
tags
为这个扩展支持的一组标签名。parse
为一个函数,当编译时会解析模板。除此之外,还有一个特殊的节点名为 CallExtension
,在运行时你可以调用本扩展的其他方法,下面会详细说明。
因为你需要直接使用 parse api,并且需要手动分析初 AST,所以有一点麻烦。如果你希望做一些复杂的扩展这是必须的。所以介绍一下你会用到的方法:
parseSignature([throwErrors], [noParens])
- 解析标签的参数。默认情况下,解析器会从括号左边解析到括号右边。但是自定义标签不应该时括号,所以将第二个参数设为true
告诉解析器解析参数直到标签关闭。参数之间应该用逗号分隔,如{%
。
mytag foo, bar, baz=10 %}parseUntilBlocks(names)
- 解析内容直到下一个名为names
的标签,非常有用解析标签之间的内容。
parser API 还需要更多的文档,但现在对照上面的文档和下面的例子,你还可以看下源码。
最常用使用的是在运行时解析标签间的内容,就像过滤器一样,但是更灵活,因为不只是局限在一个表达式中。通常情况下你会解析模板,然后将内容传入回调。你可以使用 CallExtension
,需要传扩展的实例,方法名,解析的参数和内容(使用 parseUntilBlocks
解析的)。
例如,下面实现了从远程获取内容并插入的扩展:
function RemoteExtension() {
this.tags = ['remote'];
this.parse = function(parser, nodes, lexer) {
// get the tag token
var tok = parser.nextToken();
// parse the args and move after the block end. passing true
// as the second arg is required if there are no parentheses
var args = parser.parseSignature(null, true);
parser.advanceAfterBlockEnd(tok.value);
// parse the body and possibly the error block, which is optional
var body = parser.parseUntilBlocks('error', 'endtruncate');
var errorBody = null;
if(parser.skipSymbol('error')) {
parser.skip(lexer.TOKEN_BLOCK_END);
errorBody = parser.parseUntilBlocks('endremote');
}
parser.advanceAfterBlockEnd();
// See above for notes about CallExtension
return new nodes.CallExtension(this, 'run', args, [body, errorBody]);
};
this.run = function(context, url, body, errorBody) {
var id = 'el' + Math.floor(Math.random() * 10000);
var ret = new nunjucks.runtime.SafeString('<div id="' + id + '">' + body() + '</div>');
var ajax = new XMLHttpRequest();
ajax.onreadystatechange = function() {
if(ajax.readyState == 4) {
if(ajax.status == 200) {
document.getElementById(id).innerHTML = ajax.responseText;
}
else {
document.getElementById(id).innerHTML = errorBody();
}
}
};
ajax.open('GET', url, true);
ajax.send();
return ret;
};
}
env.addExtension('RemoteExtension', new RemoteExtension());
模板可以这么写:
{% remote "/stuff" %}
This content will be replaced with the content from /stuff
{% error %}
There was an error fetching /stuff
{% endremote %}
Asynchronous
如果是异步的可以使用 CallExtensionAsync
,在运行时扩展有一个回调作为最后一个参数,模板渲染会等待回调返回。
上面例子中的 run
如下使用
this.run = function(context, url, body, errorBody, callback) {
// do async stuff and then call callback(err, res)
};
如果你做了些有趣的东西的话,请记得把他们添加到wiki中!