Handlebars - 在模板中基于数组键进行计算

7

我是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版本)如下:
  • 欧洲
    • 第1组
      • FF1A
      • FF1B
    • 第2组
      • FF2A
  • 亚洲
    • 第999组
      • FF999A
      • FF999B
  • 美洲
    • 第10000组
      • FF10000A

在不重复输出 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中有一种方法,因为模板是主要用例。


你必须使用handlebars吗?你可以用纯JS重构你的数据,更容易地输出它。 - Kobe
我明白我们可以使用jQuery - 或者甚至是原生JavaScript - 循环遍历JSON并将其写入#output。但Handlebars、Mustache等工具的一个目的是,让你可以将模板语法编写为HTML(请参见问题中#regulations-template的内部),从而获得更多的灵活性。 - Andy
是的,模板确实更容易阅读,但对于这样简单的东西,我认为纯JavaScript足够了。不过,这是你的选择,但我以前从未使用过handlebars,所以无法帮助你 :P - Kobe
问题是如何使用Handlebars完成它。感谢您的参与。 - Andy
我认为我可以解决你的问题。这里唯一的问题是你的数据结构,它可以被修复,然后你就可以使用handlebars来渲染它。 - Kobe
显示剩余5条评论
2个回答

0
我使用了一个递归函数(准备好了一行很长的代码)来格式化数据,以便更容易地显示。您只需映射子项,并使用标签进行打印:

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>

注意,我删除了jQuery,因为我觉得它不是必需的,但是如果需要可以随时添加回来 :)

0

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

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

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接