译注:
1. 一般来说context,翻译为【上下文】,在 JS 中多数时候就是指函数执行时的 this 的引用。
2. 看,不是最重要的,多写代码,照着例子写写,再改改,看看结果,就知道怎么回事了。
块级 helpers 使用自定义的迭代器,其他的 helpers 都可以使用一个新的上下文来执行内部的代码块。
基础
先定义一个简单的块级 helper,它只是简单的执行一下这块代码,就跟没有用这个helper一样。
<div class="entry">
<h1>{{title}}</h1>
<div class="body">
{{#noop}}{{body}}{{/noop}}
</div>
</div>
这个 noop
helper 接受一个 options 对象。这个对象有一个叫 options.fn
的方法,这个方法的用法就跟普通的编译过的 Hanldebars 模板的用法一样。所不同的就是,这个函数执行时会带有一个上下文,并且会返回一个字符串。
Handlebars.registerHelper('noop', function(options) {
return options.fn(this);
});
Handlebars 在执行 helpers 的时候,总是会将 this
指向当前的上下文,所以,你只需用 this
来调用这一块代码,就可以在当前的上下文中求值了。
with
helper
在上面介绍的 noop
helper 的基础上,很容易的就能想到应该如何实现 with
helper 了。这里呢,不是用当前的上下文来执行代码块,我们可以使用任意的模板所传递进来的上下文。
<div class="entry">
<h1>{{title}}</h1>
{{#with story}}
<div class="intro">{{{intro}}}</div>
<div class="body">{{{body}}}</div>
{{/with}}
</div>
在你使用的JSON嵌套非常深的时候,就会觉得这个helper 非常有帮助了,否则的话,每次都需要先写上很长的父级属性。比如上面的模板可以使用下面的这段JSON:
{
title: "First Post",
story: {
intro: "Before the jump",
body: "After the jump"
}
}
实现这种helper,就跟实现一堆 noop
helper 没什么区别。要注意的是,helpers 可以接受参数,并且对参数的求值就跟直接使用 {{mustache}}
表达式一样一样地。
Handlebars.registerHelper('with', function(context, options) {
return options.fn(context);
});
helpers 接受参数的顺序跟传递进来的顺序相同。
简单的迭代器
使用块级 helpers 的一个非常常见的目的就是定义一个自定义的迭代器。实际上,所有的 Handlebars 内置的 helpers 就是一些常规的 块级 helpers。我们一起来看看内置的 each
helper 是如何工作的:
<div class="entry">
<h1>{{title}}</h1>
{{#with story}}
<div class="intro">{{{intro}}}</div>
<div class="body">{{{body}}}</div>
{{/with}}
</div>
<div class="comments">
{{#each comments}}
<div class="comment">
<h2>{{subject}}</h2>
{{{body}}}
</div>
{{/each}}
</div>
在这里的情况下,我们希望对 comments 数组的每一项都执行 传递给 each
的这一块代码。
Handlebars.registerHelper('each', function(context, options) {
var ret = "";
for(var i=0, j=context.length; i<j; i++) {
ret = ret + options.fn(context[i]);
}
return ret;
});
这样,我们迭代传进来的参数的每一个项,并在每一次迭代的时候执行一次块中的代码。在我们迭代的过程中,还构建了一个字符串,并最终返回这个字符串。
可以看出,你也可以很容易的实现一个更高级的迭代器。例如,我们创建一个迭代器,可以生成 <ul>
的包裹容器,并且把每一个元素的计算结果包裹到<li>
中去。
{{#list nav}}
<a href="{{url}}">{{title}}</a>
{{/list}}
可以使用类似下面的上下文来执行模板:
{
nav: [
{ url: "http://www.yehudakatz.com", title: "Katz Got Your Tongue" },
{ url: "http://www.sproutcore.com/block", title: "SproutCore Blog" },
]
}
这个 helper 跟内置的 each
helper 没有太大的不同。
Handlebars.registerHelper('list', function(context, options) {
var ret = "<ul>";
for(var i=0, j=context.length; i<j; i++) {
ret = ret + "<li>" + options.fn(context[i]) + "</li>";
}
return ret + "</ul>";
});
你当然也可以使用类似 underscore.js 或 SproutCore's 的类库,让代码看起来更精简漂亮些。使用 SproutCore's 的示例如下:
Handlebars.registerHelper('list', function(context, options) {
return "<ul>" + context.map(function(item) {
return "<li>" + options.fn(item) + "</li>";
}).join("\n") + "</ul>";
});
条件
使用 helpers 的另一个常见需求就是实现条件判断。
再次重申一遍,Handlebars 内置的 if
和 unless
控制结构就是使用常规的 helpers 来实现的。
{{#if isActive}}
<img src="star.gif" alt="Active">
{{/if}}
控制结构一般不会改变上下文,只是要根据一些变量来决定是否要执行这一块代码。
Handlebars.registerHelper('if', function(conditional, options) {
if(conditional) {
return options.fn(this);
}
});
在写控制结构的时候,经常需要在模板中提供一块 条件求值返回false时 执行的模板代码。Handlebars 给 helpers 提供了通用的 else
功能来解决这个问题。
{{#if isActive}}
<img src="star.gif" alt="Active">
{{else}}
<img src="cry.gif" alt="Inactive">
{{/if}}
Handlebars 以 options.inverse
的形式来支持 else
块中的代码。如果模板没有提供条件取反的模板,Handlebars会自动的创建一个 空函数,这样你就不必去检查 inverse 是否存在了。
Handlebars.registerHelper('if', function(conditional, options) {
if(conditional) {
return options.fn(this);
} else {
return options.inverse(this);
}
});
Handlebars 还给 helpers 提供了额外的元数据,附加在 options 对象上面。继续阅读下面的例子。
哈希参数
就像普通的 helpers,块级 helpers 可以接受一个可选的对象作为最后一个参数。我们一起重温一下 list
helper,并让他可以接受任意数量的可选属性,并且把添加到我们要创建的 <ul>
元素上。
{{#list nav id="nav-bar" class="top"}}
<a href="{{url}}">{{title}}</a>
{{/list}}
Handlebars 以 options.hash
的形式把最后传进来的键值对挂在上来。这样就更容易接受不定数量的参数了,同时接受一个可选的对象。如果模板没有提供哈希参数,Handlebars 会自动的传一个空对象({}
),这样你就不必检查哈希参数是否存在了。
Handlebars.registerHelper('list', function(context, options) {
var attrs = Em.keys(options.hash).map(function(key) {
key + '="' + options.hash[key] + '"';
}).join(" ");
return "<ul " + attrs + ">" + context.map(function(item) {
return "<li>" + options.fn(item) + "</li>";
}).join("\n") + "</ul>";
});
哈希参数提供了一个强大的方法来给 helper 提供任意数量的可选参数,而且避免了由可选性导致的复杂语法。
块级helper还可以往它的子模板中注入私有变量。在一些情况下会很有帮助,比如需要把一些不在原来上下文中的数据插进来。
例如,在循环一个列表的时候,你需要以私有变量的形式提供当前的索引index值。
{{#list array}}
{{@index}}. {{title}}
{{/list}}
Handlebars.registerHelper('list', function(context, options) {
var out = '
<
ul>', data;
for (var i=0; i<context.length; i++) {
if (options.data) {
data = Handlebars.createFrame(options.data || {});
data.index = i;
}
out += "<li>" + options.fn(context[i], { data: data }) + "</li>";
}
out += "</ul>";
return out;
});
私有属性通过 data
选项来提供,并且在所有的内部作用域内都可以使用。
要保证每次带着 data 执行代码块的时候都生成一个新的 data 帧。否则的话,后面的 helpers 可能会意外的改变前面的变量。
还要确保在试图跟已存在的 data 对象交互之前 data
对象已经存在了。私有变量的行为是有条件编译的,有一些模板可能并没有创建这个字段。
空白管理
模板中的空白可以忽略,mustache声明的两边都可以,只需添加一个 ~
字符即可。写了这个之后,这一边的所有空白都会被移除,直到最近的Handlebars表达式或这一边的非空白字符。
{{#each nav ~}}
<a href="{{url}}">
{{~#if test}}
{{~title}}
{{~^~}}
Empty
{{~/if~}}
</a>
{{~/each}}
用这个上下文:
{
nav: [
{url: 'foo', test: true, title: 'bar'},
{url: 'bar'}
]
}
得出的结果没有换行,也没有格式化用的空白符:
<a href="foo">bar</a><a href="bar">Empty</a>
转载请注明来自超2真人
本文链接:http://www.peichao01.com/static_content/doc/html/introduce-handlebars-helpers.html
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。