Spacebars 模板

Spacebars 是一种类似于 Handlebars 的模板语言,它基于渲染反应式变化的数据上下文的概念。Spacebars 模板看起来像简单的 HTML,带有特殊的“mustache”标签,由花括号分隔:{{ }}

例如,考虑 Todos 示例应用程序中的Todos_item 模板

1
2
3
4
5
6
7
8
9
10
11
12
13
<template name="Todos_item">
<div class="list-item {{checkedClass todo}} {{editingClass editing}}">
<label class="checkbox">
<input type="checkbox" checked={{todo.checked}} name="checked">
<span class="checkbox-custom"></span>
</label>

<input type="text" value="{{todo.text}}" placeholder="Task name">
<a class="js-delete-item delete-item" href="#">
<span class="icon-trash"></span>
</a>
</div>
</template>

此模板期望使用具有键todo的对象作为数据上下文进行渲染(我们将在下面看到如何强制执行此操作)。我们使用 mustache 标签访问todo的属性,例如{{todo.text}}。默认行为是将该属性渲染为字符串;但是,对于某些属性(例如checked={{todo.checked}}),它可以解析为布尔值。

请注意,像这样的简单字符串插值将始终为您转义任何 HTML,因此您不需要执行 XSS 的安全检查。

此外,我们可以看到模板助手的示例——{{checkedClass todo}}调用在单独的 JavaScript 文件中定义的checkedClass助手。HTML 模板和 JavaScript 文件一起定义了Todos_item组件

1
2
3
4
5
Template.Todos_item.helpers({
checkedClass(todo) {
return todo.checked && 'checked';
}
});

在 Blaze 助手上下文中,this的作用域是使用助手时的当前数据上下文。这可能难以理解,因此通常最好将所需数据作为参数传递给助手(就像我们在这里做的那样)。

除了简单插值之外,mustache 标签还可用于模板中的控制流。例如,在Lists_show模板中,我们这样渲染 todos 列表

1
2
3
4
5
6
7
8
{{#each todo in todos}}
{{> Todos_item (todoArgs todo)}}
{{else}}
<div class="wrapper-message">
<div class="title-message">No tasks here</div>
<div class="subtitle-message">Add new tasks using the field above</div>
</div>
{{/each}}

此代码段说明了几件事

  • {{#each .. in}}块助手,它为数组或游标中的每个元素重复一个 HTML 块,或者在没有项目存在时渲染{{else}}块的内容。
  • 模板包含标签{{> Todos_item (todoArgs todo)}},它使用todosArg助手返回的数据上下文渲染Todos_item组件。

您可以在Spacebars 文档中阅读有关完整语法的详细信息。在本节中,我们将尝试介绍一些超出语法本身的重要细节。

数据上下文和查找

我们已经看到{{todo.title}}访问当前数据上下文上todo项的title属性。此外,..访问父数据上下文(很少是一个好主意),list.todos.[0]访问listtodos数组的第一个元素。

请注意,Spacebars 对null值非常宽容。如果您尝试访问null值的属性(例如,如果foo未定义,则为foo.bar),它不会抱怨,而是将其简单地视为 null。但是,对此有一些例外——尝试调用null函数,或在助手内部执行相同的操作将导致异常。

使用参数调用助手

您可以像checkedClass一样为助手提供参数,只需将参数放在助手调用之后,例如:{{checkedClass todo true 'checked'}}。您还可以使用{{checkedClass todo noClass=true classname='checked'}}为助手提供命名关键字参数列表。当您传递关键字参数时,您需要从最终参数的hash属性中读取它们。以下是我们刚才看到的示例

1
2
3
4
5
6
7
8
9
10
Template.Todos_item.helpers({
checkedClass(todo, options) {
const classname = options.hash.classname || 'checked';
if (todo.checked) {
return classname;
} else if (options.hash.noClass) {
return `no-${classname}`;
}
}
});

请注意,使用关键字参数传递助手有点笨拙,因此通常最好避免它们。此功能包含在内是为了历史原因,以匹配关键字参数在 Handlebars 中的工作方式。

您还可以将助手的输出传递给模板包含或其他助手。为此,请使用括号来显示优先级

1
{{> Todos_item (todoArgs todo)}}

这里将todo作为参数传递给todoArgs助手,然后将输出传递到Todos_item模板中。

模板包含

您使用{{> }}语法“包含”一个子组件。默认情况下,子组件将获得调用者的数据上下文,但通常最好明确说明。您可以提供一个将成为整个数据上下文的单个对象(就像我们在上面使用todoArgs助手返回的对象一样),或者提供一个关键字参数列表,这些参数将被组合成一个对象,如下所示

1
{{> subComponent arg1="value-of-arg1" arg2=helperThatReturnsValueOfArg2}}

在这种情况下,subComponent组件可以预期以下形式的数据上下文

1
2
3
4
{
arg1: ...,
arg2: ...
}

属性助手

我们在上面看到,使用形式为checked={{todo.checked}}的助手(或数据上下文查找)将在todo.checked评估为 true 时将 checked 属性添加到 HTML 标签。此外,您还可以直接将对象包含在 HTML 元素的属性列表中以一次设置多个属性

1
<a {{attributes}}>My Link</a>
1
2
3
4
5
6
7
8
Template.foo.helpers({
attributes() {
return {
class: 'A class',
style: {background: 'blue'}
};
}
});

渲染原始 HTML

虽然默认情况下 mustache 标签会转义 HTML 标签以避免XSS,但您可以使用三连字符渲染原始 HTML:{{{ }}}

1
{{{myHtml}}}
1
2
3
4
5
Template.foo.helpers({
myHtml() {
return '<h1>This H1 will render</h1>';
}
});

您应该非常小心地执行此操作,并始终确保您没有从这样的助手返回用户生成的内容(或者如果您这样做,请转义它!)。

块助手

块助手,使用{{# }}调用,是一个助手,它接受(并可能渲染)一个 HTML 块。例如,我们在上面看到了{{#each .. in}}助手,它对列表中的每个项目重复给定的 HTML 块。您还可以使用模板作为块助手,通过Template.contentBlockTemplate.elseBlock渲染其内容。例如,您可以使用以下方法创建自己的{{#if}}助手

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<template name="myIf">
{{#if condition}}
{{> Template.contentBlock}}
{{else}}
{{> Template.elseBlock}}
{{/if}}
</template>

<template name="caller">
{{#myIf condition=true}}
<h1>I'll be rendered!</h1>
{{else}}
<h1>I won't be rendered</h1>
{{/myIf}}
</template>

内置块助手

有一些内置的块助手值得了解

如果 / 除非

{{#if}}{{#unless}}助手非常简单直观,但对于控制模板的控制流至关重要。两者都通过评估其单个参数并检查其真值来工作。在 JS 中,nullundefined0''NaNfalse被认为是“假值”,所有其他值都被认为是“真值”。

1
2
3
4
5
{{#if something}}
<p>It's true</p>
{{else}}
<p>It's false</p>
{{/if}}

每个-在

{{#each .. in}}助手是在保留外部数据上下文的情况下遍历列表的便捷方式。

1
2
3
4
5
{{#each todo in todos}}
{{#each tag in todo.tags}}
<!-- in here, both todo and tag are in scope -->
{{/each}}
{{/each}}

{{#let}}助手有助于在模板中捕获助手的输出或文档子属性。将其视为使用 JavaScript let定义变量一样。

1
2
3
{{#let name=person.bio.firstName color=generateColor}}
<div>{{name}} gets a {{color}} card!</div>
{{/let}}

请注意,namecolor(以及上面的todo)仅在模板作用域中添加;它们不会添加到数据上下文中。具体来说,这意味着在助手和事件处理程序中,您无法使用this.namethis.color访问它们。如果您需要在助手内部访问它们,则应将它们作为参数传递(就像我们在上面的(todoArgs todo)中所做的那样)。

每个和与

还有两个 Spacebars 内置助手{{#each}}{{#with}},我们不建议使用它们(请参阅建议使用 each-in)。这些块助手会在模板中更改数据上下文,这可能难以理解。

{{#each .. in}}类似,{{#each}}会遍历数组或游标,将内容块中的数据上下文更改为当前迭代中的项目。{{#with}}只是将自身内部的数据上下文更改为提供的对象。在大多数情况下,最好使用{{#each .. in}}{{#let}},就像使用变量声明比使用 JavaScript with关键字更好一样。

块助手的链接

您可以链接块助手

1
2
3
4
5
6
7
{{#input isRadio}}
<input type="radio" />
{{else input isCheckbox}}
<input type="checkbox" />
{{else}}
<input type="text" />
{{/foo}}

这等效于

1
2
3
4
5
6
7
8
9
{{#input isRadio}}
<input type="radio" />
{{else}}
{{#input isCheckbox}}
<input type="checkbox" />
{{else}}
<input type="text" />
{{/input}}
{{/input}}

严格性

Spacebars 具有非常严格的 HTML 解析器。例如,您不能在 Spacebars 中自闭合div<div/>),并且您需要关闭浏览器可能不需要关闭的某些标签(例如<p>标签)。值得庆幸的是,解析器会在无法理解您的代码时用错误的确切行号发出警告。

转义

要插入文字花括号:{{ }}等,请在起始花括号中添加一个管道字符|

1
2
3
4
5
<!-- will render as <h1>All about {{</h1> -->
<h1>All about {{|</h1>

<!-- will render as <h1>All about {{{</h1> -->
<h1>All about {{{|</h1>
在 GitHub 上编辑