# 块助手

¥Block Helpers

块助手使得定义自定义迭代器和其他功能成为可能,这些功能可以使用新的上下文调用传递的块。

¥Block helpers make it possible to define custom iterators and other functionality that can invoke the passed block with a new context.

# 基本块

¥Basic Blocks

出于演示目的,我们定义一个块助手来调用该块,就好像不存在助手一样。

¥For demonstration purposes, let's define a block helper that invokes the block as though no helper existed.

<div class="entry">
  <h1>{{title}}</h1>
  <div class="body">
    {{#noop}}{{body}}{{/noop}}
  </div>
</div>

noop 助手("无操作" 的缩写)将收到选项哈希。此选项哈希包含一个函数 (options.fn),其行为类似于正常编译的 Handlebars 模板。具体来说,该函数将获取上下文并返回一个字符串。

¥The noop helper (short for "no operation") will receive an options hash. This options hash contains a function (options.fn) that behaves like a normal compiled Handlebars template. Specifically, the function will take a context and return a String.

Handlebars.registerHelper("noop", function(options) {
  return options.fn(this);
});

Handlebars 始终使用当前上下文作为 this 调用辅助程序,因此你可以使用 this 调用该块来评估当前上下文中的块。

¥Handlebars always invokes helpers with the current context as this, so you can invoke the block with this to evaluate the block in the current context.

以这种方式定义的任何助手将优先于上下文中定义的字段。为了访问被助手屏蔽的字段,可以使用路径引用。在上面的示例中,将使用以下命令引用上下文对象上名为 noop 的字段:

¥Any helpers defined in this manner will take precedence over fields defined in the context. To access a field that is masked by a helper, a path reference may be used. In the example above a field named noop on the context object would be referenced using:

{{./noop}}

# 基本块变化

¥Basic Block Variation

为了更好地说明语法,让我们定义另一个块助手,它向封装文本添加一些标记。

¥To better illustrate the syntax, let's define another block helper that adds some markup to the wrapped text.

<div class="entry">
  <h1>{{title}}</h1>
  <div class="body">
    {{#bold}}{{body}}{{/bold}}
  </div>
</div>

粗体助手将添加标记以使其文本变为粗体。和以前一样,该函数将采用上下文作为输入并返回一个字符串。

¥The bold helper will add markup to make its text bold. As before, the function will take a context as input and return a String.

Handlebars.registerHelper("bold", function(options) {
  return new Handlebars.SafeString('<div class="mybold">' + options.fn(this) + "</div>");
});

# with 助手

¥The with helper

with 辅助程序演示了如何将参数传递给你的辅助程序。当使用参数调用助手时,它会使用模板传入的任何上下文来调用。

¥The with helper demonstrates how to pass a parameter to your helper. When a helper is called with a parameter, it is invoked with whatever context the template passed in.

<div class="entry">
  <h1>{{title}}</h1>
  {{#with story}}
    <div class="intro">{{{intro}}}</div>
    <div class="body">{{{body}}}</div>
  {{/with}}
</div>

如果 JSON 对象的一部分包含深度嵌套的属性,并且你希望避免重复父名称,你可能会发现这样的辅助程序很有用。上面的模板对于如下 JSON 很有用:

¥You might find a helper like this useful if a section of your JSON object contains deeply nested properties, and you want to avoid repeating the parent name. The above template could be useful with a JSON like:

{
  title: "First Post",
  story: {
    intro: "Before the jump",
    body: "After the jump"
  }
}

实现这样的助手很像实现 noop 助手。助手可以接受参数,并且参数的计算就像直接在 {{mustache}} 块内使用的表达式一样。

¥Implementing a helper like this is a lot like implementing the noop helper. Helpers can take parameters, and parameters are evaluated just like expressions used directly inside {{mustache}} blocks.

Handlebars.registerHelper("with", function(context, options) {
  return options.fn(context);
});

参数按照传递的顺序传递给助手,后跟选项哈希。

¥Parameters are passed to helpers in the order that they are passed, followed by the options hash.

# 简单迭代器

¥Simple Iterators

块助手的一个常见用例是使用它们来定义自定义迭代器。事实上,所有 Handlebars 内置助手都被定义为常规 Handlebars 块助手。我们来看看内置的 each 助手是如何工作的。

¥A common use-case for block helpers is using them to define custom iterators. In fact, all Handlebars built-in helpers are defined as regular Handlebars block helpers. Let's take a look at how the built-in each helper works.

<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 的块。

¥In this case, we want to invoke the block passed to each once for each element in the comments Array.

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;
});

在这种情况下,我们迭代传递参数中的项目,对每个项目调用一次块。当我们迭代时,我们构建一个字符串结果,然后返回它。

¥In this case, we iterate over the items in the passed parameter, invoking the block once with each item. As we iterate, we build up a String result, and then return it.

该模式可用于实现更高级的迭代器。例如,让我们创建一个迭代器,它创建 <ul> 封装器,并将每个结果元素封装在 <li>.

¥This pattern can be used to implement more advanced iterators. For instance, let's create an iterator that creates a <ul> wrapper, and wraps each resulting element in an <li>.

{{#list nav}}
  <a href="{{url}}">{{title}}</a>
{{/list}}

你可以使用类似以下内容作为上下文来评估此模板:

¥You would evaluate this template using something like this as the context:

{
  nav: [
    { url: "http://www.yehudakatz.com", title: "Katz Got Your Tongue" },
    { url: "http://www.sproutcore.com/block", title: "SproutCore Blog" }
  ];
}

该助手与原始 each 助手类似。

¥The helper is similar to the original 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 的运行时库这样的库可以让这个变得更好看。例如,使用 SproutCore 的运行时库可能如下所示:

¥Using a library like underscore.js or SproutCore's runtime library could make this a bit prettier. For example, here's what it might look like using SproutCore's runtime library:

Handlebars.registerHelper("list", function(context, options) {
  return (
    "<ul>" +
    context
      .map(function(item) {
        return "<li>" + options.fn(item) + "</li>";
      })
      .join("\n") +
    "</ul>"
  );
});

# 条件句

¥Conditionals

块助手的另一个常见用例是评估条件语句。与迭代器一样,Handlebars 的内置 ifunless 控制结构是作为常规 Handlebars 助手实现的。

¥Another common use-case for block helpers is to evaluate conditional statements. As with the iterators, Handlebars' built-in if and unless control structures are implemented as regular Handlebars helpers.

{{#if isActive}}
  <img src="star.gif" alt="Active">
{{/if}}

控制结构通常不会更改当前上下文,而是根据某些变量决定是否调用该块。

¥Control structures typically do not change the current context, instead they decide whether or not to invoke the block based upon some variable.

Handlebars.registerHelper("if", function(conditional, options) {
  if (conditional) {
    return options.fn(this);
  }
});

编写条件时,你通常希望模板能够提供一个 HTML 块,如果条件计算结果为 false,则助手应插入该 HTML 块。Handlebars 通过为块助手提供通用 else 功能来解决这个问题。

¥When writing a conditional, you will often want to make it possible for templates to provide a block of HTML that your helper should insert if the conditional evaluates to false. Handlebars handles this problem by providing generic else functionality to block helpers.

{{#if isActive}}
  <img src="star.gif" alt="Active">
{{else}}
  <img src="cry.gif" alt="Inactive">
{{/if}}

Handlebars 为 else 片段提供块作为 options.inverse。你不需要检查 else 片段是否存在:Handlebars 会自动检测并注册 "noop" 功能。

¥Handlebars provides the block for the else fragment as options.inverse. You do not need to check for the existence of the else fragment: Handlebars will detect it automatically and register a "noop" function.

Handlebars.registerHelper("if", function(conditional, options) {
  if (conditional) {
    return options.fn(this);
  } else {
    return options.inverse(this);
  }
});

Handlebars 通过将其附加为选项哈希的属性来为块助手提供额外的元数据。继续阅读更多示例。

¥Handlebars provides additional metadata to block helpers by attaching them as properties of the options hash. Keep reading for more examples.

条件语句也可以通过在 else 小胡子中包含后续的辅助程序调用来链接。

¥Conditionals may also be chained by including the subsequent helper call within the else mustache.

{{#if isActive}}
  <img src="star.gif" alt="Active">
{{else if isInactive}}
  <img src="cry.gif" alt="Inactive">
{{/if}}

没有必要在后续调用中使用相同的辅助程序,除非辅助程序可以像任何其他辅助程序一样在 else 部分中使用。当辅助程序值不同时,结束胡须应与开始辅助程序名称匹配。

¥It is not necessary to use the same helper in subsequent calls, the unless helper could be used in the else portion as with any other helper. When the helper values are different, the closing mustache should match the opening helper name.

# 哈希参数

¥Hash Arguments

与常规助手一样,块助手可以接受可选的哈希作为其最终参数。让我们重新审视 list 辅助程序,使我们能够向我们将创建的 <ul> 元素添加任意数量的可选属性。

¥Like regular helpers, block helpers can accept an optional Hash as its final argument. Let's revisit the list helper and make it possible for us to add any number of optional attributes to the <ul> element we will create.

{{#list nav id="nav-bar" class="top"}}
  <a href="{{url}}">{{title}}</a>
{{/list}}

Handlebars 提供的最终哈希值为 options.hash。这使得更容易接受可变数量的参数,同时也接受可选的哈希值。如果模板没有提供哈希参数,Handlebars 将自动传递一个空对象 ({}),因此你不需要检查哈希参数是否存在。

¥Handlebars provides the final hash as options.hash. This makes it easier to accept a variable number of parameters, while also accepting an optional Hash. If the template provides no hash arguments, Handlebars will automatically pass an empty object ({}), so you don't need to check for the existence of hash arguments.

Handlebars.registerHelper("list", function(context, options) {
  var attrs = Object.keys(options.hash)
    .map(function(key) {
      return key + '="' + options.hash[key] + '"';
    })
    .join(" ");

  return (
    "<ul " +
    attrs +
    ">" +
    context
      .map(function(item) {
        return "<li>" + options.fn(item) + "</li>";
      })
      .join("\n") +
    "</ul>"
  );
});

哈希参数提供了一种强大的方法,可以向块助手提供许多可选参数,而不会产生位置参数带来的复杂性。

¥Hash arguments provide a powerful way to offer a number of optional parameters to a block helper without the complexity caused by positional arguments.

块助手还可以将私有变量注入到其子模板中。这对于添加原始上下文数据中没有的额外信息非常有用。

¥Block helpers can also inject private variables into their child templates. This can be useful to add extra information that is not in the original context data.

例如,当迭代列表时,你可以提供当前索引作为私有变量。

¥For example, when iterating over a list, you may provide the current index as a private variable.

{{#list array}}
  {{@index}}. {{title}}
{{/list}}
Handlebars.registerHelper("list", function(context, options) {
  var out = "<ul>",
    data;

  if (options.data) {
    data = Handlebars.createFrame(options.data);
  }

  for (var i = 0; i < context.length; i++) {
    if (data) {
      data.index = i;
    }

    out += "<li>" + options.fn(context[i], { data: data }) + "</li>";
  }

  out += "</ul>";
  return out;
});

通过 data 选项提供的私有变量在所有后代作用域中都可用。

¥Private variables provided via the data option are available in all descendent scopes.

父作用域中定义的私有变量可以通过路径查询来访问。为了访问父迭代器的 index 字段,可以使用 @../index

¥Private variables defined in parent scopes may be accessed via pathed queries. To access the index field of the parent iterator, @../index may be used.

确保在每个助手中创建一个新的数据框来分配自己的数据。否则,下游助手可能会意外地改变上游变量。

¥Make sure you create a new data frame in each helper that assigns its own data. Otherwise, downstream helpers might unexpectedly mutate upstream variables.

还要确保在尝试与现有数据对象交互之前定义 data 字段。私有变量行为是有条件编译的,某些模板可能不会创建此字段。

¥Also ensure that the data field is defined prior to attempting to interact with an existing data object. The private variable behavior is conditionally compiled and some templates might not create this field.

# 块参数

¥Block Parameters

Handlebars 3.0 中的新增功能可以从支持助手接收命名参数。

¥New in Handlebars 3.0, it's possible to receive named parameters from supporting helpers.

{{#each users as |user userId|}}
  Id: {{userId}} Name: {{user.name}}
{{/each}}

在此特定示例中,user 将具有与当前上下文相同的值,而 userId 将具有迭代的索引值。

¥In this particular example, user will have the same value as the current context and userId will have the index value for the iteration.

这允许嵌套辅助程序避免私有变量可能发生的名称冲突。

¥This allows for nested helpers to avoid name conflicts that can occur with private variables.

{{#each users as |user userId|}}
  {{#each user.book as |book bookId|}}
    User Id: {{userId}} Book Id: {{bookId}}
  {{/each}}
{{/each}}

许多 内置助手 支持块参数和任何自定义辅助程序都可以通过 blockParams 选项字段提供它们。

¥A number of builtin helpers support block parameters and any custom helper may provide them through the blockParams options field.

助手可以通过 options.fn.blockParams 字段确定模板引用的块参数的数量,这是一个整数。该值表示子模板可以引用的块参数的数量。超出此计数的参数将永远不会被引用,并且如果需要,辅助程序可以安全地省略。这是可选的,传递给模板的任何其他参数都将被默默忽略。

¥Helpers can determine the number of block parameters referenced by the template via the options.fn.blockParams field, which is an integer count. This value represents the number of block parameters that could be referenced by the child template. Parameters beyond this count will never be referenced and can safely be omitted by the helper if desired. This is optional and any additional parameters passed to the template will be silently ignored.

# 原始块

¥Raw Blocks

原始块可用于需要处理未处理的胡须块的模板。

¥Raw blocks are available for templates needing to handle unprocessed mustache blocks.

template
{{{{raw-loud}}}}
  {{bar}}
{{{{/raw-loud}}}}
{{{{raw-helper}}}}
  {{bar}}
{{{{/raw-helper}}}}

将执行助手 raw 辅助程序而不解释内容。

¥will execute the helper raw-helper without interpreting the content.

Handlebars.registerHelper("raw-helper", function(options) {
  return options.fn();
});

将渲染

¥will render

Last Updated: 2024/4/16 21:37:19