Handlebars 模板中遍历嵌套字典数据的完整实践指南

10次阅读

Handlebars 模板中遍历嵌套字典数据的完整实践指南

本文详解如何使用 Handlebars 的 @key 数据变量,无需预处理或 lookup 辅助,直接渲染三层嵌套对象(类别 → 应用 → 告警类型 → 告警数组)为结构化 HTML 表格,适用于邮件通知等动态内容场景。

本文详解如何使用 handlebars 的 `@key` 数据变量,无需预处理或 lookup 辅助,直接渲染三层嵌套对象(类别 → 应用 → 告警类型 → 告警数组)为结构化 html 表格,适用于邮件通知等动态内容场景。

在构建用户订阅类 HTML 邮件(如应用告警汇总)时,后端常以深度嵌套字典形式组织数据:顶层为业务类别(如 “Requests” 和 “Services”),中间层为动态应用名称(如 “Division Map Layers”),底层为告警类型键(如 “Upvotes”、”Tagged Users”),其值为告警对象数组。Handlebars 原生支持这种结构——关键在于善用 @key 数据变量,它能在 {{#each}} 迭代对象时自动捕获当前键名,从而避免冗余的数据扁平化或自定义 Helper。

核心模板逻辑:三层 {{#each}} + @key 驱动标题

Handlebars 不支持直接遍历对象键名(如 JavaScript 的 Object.keys()),但 {{#each obj}} 在作用于普通对象时,会隐式迭代其可枚举属性值;此时 @key 即为对应属性名。我们据此构建三级嵌套循环:

  1. 第一层 :遍历根对象({{#each this}}),@key 为 “Requests” 或 “Services”
  2. 第二层 :遍历每个类别下的应用对象({{#each this}}),@key 为 “Division Map Layers” 等应用名
  3. 第三层 :遍历每个应用下的告警类型对象({{#each this}}),@key 为 “Upvotes”、”Messages” 等类型名
  4. 第四层(数组):对 this(即告警数组)再次 {{#each this}},直接访问 alert_message 和 createdAt

以下是完整、可运行的模板代码(含语义化 CSS):

<script id="Template" type="text/template"> <div class="mega-menu">   {{#each this}}     <div>       <!-- 类别标题 -->       <div class="main">{{@key}}</div>       <!-- 遍历该类别下的所有应用 -->       {{#each this}}         <div class="app-div">           <!-- 应用标题 -->           <div class="app-name">{{@key}}</div>           <!-- 遍历该应用下的所有告警类型 -->           {{#each this}}             <div class="alert-name">{{@key}}</div>             <div class="app-div">               <!-- 告警列表表格 -->               <table>                 <thead>                   <tr>                     <th style="width: 600px">Message</th>                     <th style="width: 100px">Date</th>                   </tr>                 </thead>                 <tbody>                   <!-- 遍历当前告警类型的每条记录 -->                   {{#each this}}                     <tr>                       <td>{{alert_message}}</td>                       <td>{{createdAt}}</td>                     </tr>                   {{/each}}                 </tbody>               </table>             </div>           {{/each}}         </div>       {{/each}}     </div>   {{/each}} </div> </script>  <style> .mega-menu {border: 1px solid aquamarine; padding: 8px; font-family: sans-serif;} .main {font-size: 24px; font-weight: bold; color: red; margin-bottom: 16px;} .app-div {margin-left: 20px;} .app-name {font-size: 18px; font-weight: bold; color: blue; margin-bottom: 8px;} .alert-name {font-size: 16px; font-weight: bold; color: green; margin-bottom: 4px;} table {width: 100%; margin-bottom: 8px;} table, th, td {border: 1px solid lightgrey; border-collapse: collapse;} th, td {padding: 2px 4px;} th {font-size: 12px; text-align: left; color: grey;} </style>  <script src="https://cdnjs.cloudflare.com/ajax/libs/handlebars.js/4.7.7/handlebars.min.js"></script> <script>   const template = Handlebars.compile(document.getElementById('Template').innerHTML);    // 示例数据(与问题中完全一致)const data = {"Requests": { /* …… */},     "Services": {/* …… */}   };    document.body.innerHTML = template(data); </script>

注意事项与最佳实践

  • 无需重构数据 :本方案完美适配原始嵌套字典结构,不强制要求转为数组或添加 name 字段,保持后端数据契约稳定。
  • ⚠️ @key 仅在 {{#each}} 中有效 :切勿在 {{#if}} 或普通表达式中尝试 {{@key}},它只在迭代上下文中存在。
  • ? 避免命名冲突 :若数据中某层对象本身含有 key 属性(如 {key: “foo”, value: “bar”}),@key 仍指向字典键名,而非该属性值——这是预期行为,无需担心覆盖。
  • ? 邮件兼容性增强 :实际用于 HTML 邮件时,建议将 CSS 内联(使用工具如 Premailer),并为
    添加 role=”presentation” 和 cellspacing=”0″ 提升客户端兼容性。

  • ? 调试技巧 :在模板中临时插入 {{log @key}} 或 {{json this}}(需注册 log/json Helper)可快速验证当前层级数据结构。
  • 通过 @key 驱动的嵌套迭代,Handlebars 将复杂字典转化为清晰、可维护的模板逻辑。它不仅解决了多级动态标题的渲染难题,更体现了声明式模板引擎“数据即结构”的设计哲学——让开发者专注描述 what to render,而非 how to traverse

text=ZqhQzanResources