转载自 http://keenwon.com/1033.html
块级的Helpers可以在新的数据上下文下自定义迭代器和其他helpers。
下面定义一个简单的块级Helpers
<div class="entry">
<h1>{{title}}</h1>
<div class="body">
{{#noop}}{{body}}{{/noop}}
</div>
</div>
noop
helper 接受一个 option hash,这个 option 包含一个函数(option.fn
),它就和普通编译出的模板一样,接受一个数据上下文,返回一个字符串。
Handlebars.registerHelper('noop', function(options) {
return options.fn(this);
});
Handlebars可以通过 this
调用当前的数据上下文。
with
helper基于刚才的 noop
helper,with
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 会非常有用。你需要一直重复父级的名称,例如使用下面的上下文
{
title: "First Post",
story: {
intro: "Before the jump",
body: "After the jump"
}
}
with
helper 和上面的 noop
很像。接受一个参数,渲染到 {{mustache}}
块中。
Handlebars.registerHelper('with', function(context, options) {
return options.fn(context);
});
块级 helper 的一个最常见的用法是自定义迭代器。事实上,所有的内置 helper 都和块级helper一样。下面我们看一下内置的 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 数组
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;
});
传入数据上下文(context),循环它们,生成一个字符串,返回。
现在,我们很容易看出如何实现更加复杂的迭代器。例如生成一个 <ul>
列表
{{#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" },
]
}
list
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>";
});
另一个常见的应用就是条件判断。同样的,内置的 if
和 unless
像普通的块级helper一样实现。
{{#if isActive}}
<img src="star.gif" alt="Active">
{{/if}}
控制结构通常不改变当前的数据上下文,但是会根据一些变量判断是否渲染相应的块。
Handlebars.registerHelper('if', function(conditional, options) {
if(conditional) {
return options.fn(this);
}
});
当使用条件判断的时候,常常需要在判断结果为 false 的时候插入相应的 HTML 块,Handlebar 使用 else
解决这个问题。
{{#if isActive}}
<img src="star.gif" alt="Active">
{{else}}
<img src="cry.gif" alt="Inactive">
{{/if}}
Handlebar 使用 options.inverse
来生成 else
的内容,如果没有 inverse 函数,模板会自动创建一个空函数。
Handlebars.registerHelper('if', function(conditional, options) {
if(conditional) {
return options.fn(this);
} else {
return options.inverse(this);
}
});
和普通的 helper 一样,块级 helper 可以接受一个 hash 作为最后一个参数。我们来改进一下 list
helper,让 <ul>
接受任意多的属性。
{{#list nav id="nav-bar" class="top"}}
<a href="{{url}}">{{title}}</a>
{{/list}}
Handlebar 把最后的 hash 传给 opton.hash
,这样可以接受任意参数,如果模板没有传hash参数,option.hash
会是 {}
。
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 还可以传递私有变量,这样就可以传入数据上下文之外的数据。
例如,我们生成一个 list,包含当前列的序号。
{{#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
传递私有变量。
使用 ~ 会省略模板任意一侧的空格
{{#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>