原文在此:original article in herejavascript
译注:
1. 通常来讲context,翻译为【上下文】,在 JS 中多数时候就是指函数执行时的 this 的引用。
2. 看,不是最重要的,多写代码,照着例子写写,再改改,看看结果,就知道怎么回事了。html
块级 helpers 使用自定义的迭代器,其余的 helpers 均可以使用一个新的上下文来执行内部的代码块。
先定义一个简单的块级 helper,它只是简单的执行一下这块代码,就跟没有用这个helper同样。java
<div class="entry"> <h1>{{title}}</h1> <div class="body"> {{#noop}}{{body}}{{/noop}} </div> </div>
这个 noop
helper 接受一个 options 对象。这个对象有一个叫 options.fn
的方法,这个方法的用法就跟普通的编译过的 Hanldebars 模板的用法同样。所不一样的就是,这个函数执行时会带有一个上下文,而且会返回一个字符串。json
Handlebars.registerHelper('noop', function(options) { return options.fn(this); });
Handlebars 在执行 helpers 的时候,老是会将 this
指向当前的上下文,因此,你只需用 this
来调用这一块代码,就能够在当前的上下文中求值了。数组
with
helper在上面介绍的 noop
helper 的基础上,很容易的就能想到应该如何实现 with
helper 了。这里呢,不是用当前的上下文来执行代码块,咱们可使用任意的模板所传递进来的上下文。less
<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}}
表达式同样同样地。oop
Handlebars.registerHelper('with', function(context, options) { return options.fn(context); });
helpers 接受参数的顺序跟传递进来的顺序相同。this
使用块级 helpers 的一个很是常见的目的就是定义一个自定义的迭代器。实际上,全部的 Handlebars 内置的 helpers 就是一些常规的 块级 helpers。咱们一块儿来看看内置的 each
helper 是如何工做的:url
<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