Handlebars - 基于模板中数组键的计算

Handlebars - calculation based on array key within a template

我是 Handlebars 的新手,使用的是 4.1.2 版。我正在尝试将一些用 PHP 编写的模板移动到 Handlebars。

我的数据源是 JSON 提要,结构如下:

[
    {
        "regulations_label": "Europe",
        "groups_label": "Group 1",
        "filters_label: "FF1A"
    },
    {
        "regulations_label": "Europe",
        "groups_label": "Group 1",
        "filters_label: "FF1B"
    },
    {
        "regulations_label": "Europe",
        "groups_label": "Group 2",
        "filters_label: "FF2A"
    },
    {
        "regulations_label": "Asia",
        "groups_label": "Group 999",
        "filters_label: "FF999A"
    },
    {
        "regulations_label": "Asia",
        "groups_label": "Group 999",
        "filters_label: "FF999B"
    },
    {
        "regulations_label": "Americas",
        "groups_label": "Group 10000",
        "filters_label: "FF10000A"
    },
]

我的 HTML 模板(在 PHP 版本中)的输出如下:

实现这一目标的方式 - 在输出期间不复制任何 regulations_labelgroups_label - 是使用条件逻辑来检查以前的数组值以查看它是否已更改,例如

// $data is the JSON above. 
foreach ($data as $key => $d): 
    if ($data[$key-1]['groups_label'] !== $d['groups_label'])
        echo $d['groups_label'];
    endif;
endforeach;

上面的代码表示groups_label只有和之前的值不一样才渲染,即只能打印一次"Group 1"等

所以在 Handlebars 中我想应用相同的原则。我已阅读 Handlebars/Mustache - Is there a built in way to loop through the properties of an object? 并了解有 {{@index}}{{@key}}

我遇到的问题是我无法对这些应用条件逻辑。例如,没有 {{@index - 1}}

这样的东西

我设置的方式如下:

<script id="regulations-template" type="text/x-handlebars-template">
    {{#each myregs}}
        - {{regulations_label}} <br>
        -- {{groups_label}} <br>
        ---- {{filters_label}} <br>
    {{/each}}
</script>


<script type="text/javascript">
    var regsInfo = $('#regulations-template').html();

    var template = Handlebars.compile(regsInfo);

    var regsData = template({ myregs: // JSON data shown above });

    $('#output').html(regsData);
</script>


<div id="output"></div>

内容呈现到 #output div,但它会重复每个级别,例如

- Europe
    -- Group 1
        ---- FF1A
- Europe
    -- Group 1
        ---- FF1B

之所以会出现此问题,是因为我无法找到一种方法来查看先前的数组值是什么并执行条件逻辑。我该如何解决这个问题?

赏金注意事项:

我正在寻找一种解决方案,它使用 Handlebars 并利用它提供的任何有助于解决此问题的功能。有人评论说这种事情用普通的js/jquery是可以做到的。我们想使用 Handlebars 来利用它提供的模板,因此赏金需要一个充分利用它的解决方案。这种条件逻辑是许多其他模板系统(不仅限于 PHP)的微不足道的一部分。因此我忍不住认为 Handlebars 中有一种方法,因为模板是主要用例。

我使用了一个递归函数(准备一个很长的衬垫)以一种更易于显示的方式格式化数据。您可以只映射到子项上,然后使用标签打印:

const data = [
  { regulations_label: "Europe", groups_label: "Group 1", filters_label: "FF1A" },
  { regulations_label: "Europe", groups_label: "Group 1", filters_label: "FF1B" },
  { regulations_label: "Europe", groups_label: "Group 2", filters_label: "FF2A" },
  { regulations_label: "Asia", groups_label: "Group 999", filters_label: "FF999A" },
  { regulations_label: "Asia", groups_label: "Group 999", filters_label: "FF999B" },
  { regulations_label: "Americas", groups_label: "Group 10000", filters_label: "FF10000A" }
];

const r = (o, keys) => Object.entries(o.reduce((a, { [keys[0]]: l, ...r }) => ({ ...a, [l]: a[l] ? [...a[l], r] : [r] }), {})).map(([k, v]) => ({ label: k, children: keys.length === 2 ? v.map(o => o[keys[1]]) : r(v, keys.slice(1))}))

const myregs = r(data, ['regulations_label', 'groups_label', 'filters_label'])
const log = () => console.log(myregs)

var regsInfo = document.getElementById('regulations-template').innerHTML
var template = Handlebars.compile(regsInfo);
var regsData = template({myregs});
document.getElementById('output').innerHTML = regsData
<script src="https://cdnjs.cloudflare.com/ajax/libs/handlebars.js/4.1.2/handlebars.js"></script>
<script id="regulations-template" type="text/x-handlebars-template">
    {{#each myregs}}
      - {{this.label}} <br>
        {{#each this.children}}
          - - {{this.label}} <br>
          {{#each this.children}}
            - - - {{this}} <br>
          {{/each}}
        {{/each}}
    {{/each}}
</script>

<button onclick="log()">Log data</button>
<div id="output"></div>

Note I removed the jQuery, just because I felt it wasn't needed, but feel free to add it back in :)

您可以在纯车把解决方案中使用以下助手:

<script id="regulations-template" type="text/x-handlebars-template">
{{#each myregs}}
    {{#ifPrevRegulation}} - {{regulations_label}} <br> {{/ifPrevRegulation}}
    {{#ifPrevGroup}}-- {{groups_label}} <br>{{/ifPrevGroup}}
    {{#ifPrevFilter}} ---- {{filters_label}} <br>{{/ifPrevFilter}}
    {{setPrevContext}}
{{/each}}
</script>

在哪里

var prevContext = null;
Handlebars.registerHelper('setPrevContext', function(){
    prevContext = this;
});

Handlebars.registerHelper('ifPrevRegulation', function(options){
    if(!(prevContext  && this.regulations_label == prevContext.regulations_label)){
      return options.fn(this);
    }
});

Handlebars.registerHelper('ifPrevGroup', function(options){
    if(!(prevContext  && this.regulations_label == prevContext.regulations_label && this.groups_label== prevContext.groups_label)){
      return options.fn(this);
    }
});

Handlebars.registerHelper('ifPrevFilter', function(options){
    if(!(prevContext  && this.regulations_label == prevContext.regulations_label && this.groups_label== prevContext.groups_label && this.filters_label == prevContext.filters_label)){
      return options.fn(this);
    }
});