Handlebars.js 块级 Helpers
2014-04-03 · 1,073 chars · 6 min read
块级的 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
调用当前的数据上下文。
The 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>.html" {{/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) } })
Hash 参数#
和普通的 helper 一样,块级 helper 可以接受一个 hash 作为最后一个参数。我们来改进一下list
helper,让<ul>
接受任意多的属性。
{{#list nav id="nav-bar" class="top"}} <a href="{{url}}">{{title}}</a>.html" {{/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}}">.html" {{~#if test}} {{~title}} {{~^~}} Empty {{~/if~}} </a> {{~/each}}
使用下面的数据
{ nav: [{ url: 'foo', test: true, title: 'bar' }, { url: 'bar' }] }
结果输出无换行和空格
<a href="foo">bar</a><a.html" href="bar">Empty</a> handlebars.js中文文档