在此页面上:了解如何使用Handlebars迭代语法从上下文数据源(如事件、自定义操作响应和数据集查找)循环数组以在消息中显示动态列表。
了解如何使用Handlebars迭代语法在消息中显示来自各种源的数据的动态列表,包括事件、自定义操作响应和其他上下文数据。
概述 overview
在消息个性化期间,Journey Optimizer允许从多个来源访问上下文数据。 您可以使用本机渠道(电子邮件、推送、短信)中的Handlebars语法对这些源中的数组进行迭代,以显示动态内容,如产品列表、推荐或其他重复元素。
可用的上下文源:
本指南向您展示了如何在消息中从每个来源对阵列进行迭代,以及在配置历程活动时如何使用阵列。 从Handlebars迭代语法开始,了解消息个性化基本知识,或跳转到使用历程表达式中的数组,了解如何将数组数据传递给自定义操作和数据集查找。
Handlebars迭代语法 syntax
Handlebars提供{{#each}} 帮助程序以通过数组进行迭代。 基本语法为:
关键点:
- 将
arrayPath替换为数组数据的路径 - 将
item替换为您喜欢的任何变量名称(例如,product、response、element) - 使用
{{item.propertyName}}访问每个项目的属性 - 您可以为多级数组嵌套多个
{{#each}}块
对事件数据进行迭代 event-data
当您的历程由事件触发时,事件数据可用。 这有助于显示历程开始时捕获的数据,例如购物车内容、订单项目或表单提交。
事件的上下文路径
<event_ID>:历程中配置的事件唯一ID<fieldPath>:事件架构中字段或数组的路径
1697323153),请在表达式路径中以反撇号(`)将其换行。 如果没有反撇号,PQL解析器会引发语法错误。| code language-handlebars |
|---|
|
示例:事件中的购物车项目
如果您的事件架构包含productListItems数组(标准XDM格式),您可以显示购物车内容,如下面的示例中详述。
| code language-handlebars |
|---|
|
示例:事件中的嵌套数组
对于嵌套结构,使用嵌套的{{#each}}块。
| code language-handlebars |
|---|
|
在最佳实践中了解有关嵌套的更多信息。
迭代自定义操作响应 custom-action-responses
自定义操作响应包含从外部API调用返回的数据。 这对于显示系统中的实时信息非常有用,例如忠诚度点数、产品推荐、库存状态或个性化优惠。
自定义操作的上下文路径
<actionName>:历程中配置的自定义操作的名称<fieldPath>:响应有效负载中字段或数组的路径
示例:来自API的产品推荐
要迭代从自定义操作返回的一系列产品推荐,并在消息中将它们显示为单个卡片,请参阅以下示例。
API响应:
| code language-json |
|---|
|
消息个性化:
| code language-handlebars |
|---|
|
示例:自定义操作中的嵌套数组
要迭代包含嵌套数组(一个对象数组,其中每个对象都包含另一个数组)的自定义操作响应,请参阅以下示例。 此演示使用嵌套的{{#each}}循环访问多个级别的数据。
API响应:
| code language-json |
|---|
|
消息个性化:
| code language-handlebars |
|---|
|
有关更复杂的嵌套模式,请参阅最佳实践。
示例:忠诚度层级优势
要根据忠诚度状态显示动态福利,请参阅以下示例。
API响应:
| code language-json |
|---|
|
消息个性化:
| code language-handlebars |
|---|
|
对数据集查找结果进行迭代 dataset-lookup
数据集查找活动允许您在历程运行时从Adobe Experience Platform数据集中检索数据。 扩充的数据将作为数组存储,并可在消息中迭代。
在此部分中了解有关配置数据集查找活动的更多信息。 与事件数据结合使用时,数据集查找功能尤为强大 — 请参阅示例:通过数据集查找扩充了事件数据以了解实际用例。
数据集查找的上下文路径
<activityID>:数据集查找活动的唯一IDentities:从数据集中检索的扩充数据数组
示例:数据集中的产品详细信息
如果您使用数据集查找活动根据SKU检索产品信息,请参阅以下示例。
数据集查找配置:
- 查找键:
list(@event{purchase_event.products.sku}) - 要返回的字段:
["SKU", "category", "price", "name"]
消息个性化:
| code language-handlebars |
|---|
|
示例:使用数据集数据过滤的迭代
要在迭代期间筛选数据集查找结果并仅显示符合特定条件的项(例如,特定类别中的产品),请在{{#each}}循环中使用条件{{#if}}语句。 请参阅以下示例。
| code language-handlebars |
|---|
|
在最佳实践中了解有关条件筛选的更多信息。
示例:通过数据集查找计算总数
要在迭代数据集查找结果时计算并显示总计,请参阅以下示例。
| code language-handlebars |
|---|
|
使用历程技术属性 technical-properties
历程技术属性提供对旅程执行相关元数据的访问权限,例如旅程ID和补充标识符。 在与迭代模式结合使用时,这些规则可能很有用,尤其是对于根据特定历程实例筛选数组。
可用的技术属性
示例:使用补充标识符筛选数组项
在具有数组的事件触发的历程中使用补充标识符时,您可以进行筛选以仅显示当前历程实例的相关项目。 在本指南中了解有关补充标识符的更多信息。
场景:历程通过多个预订触发,但您只想显示触发此历程实例的特定预订(由补充ID标识)的信息。
| code language-handlebars |
|---|
|
示例:包含用于跟踪的历程ID
要在消息中包含历程ID以进行跟踪,请参阅以下示例。
| code language-handlebars |
|---|
|
合并多个上下文源 combine-sources
您可以将来自不同来源的数据合并到同一消息中,以创建丰富且个性化的体验。 本节显示了将多个上下文源结合使用的实际示例。
可以合并的上下文源:
示例:具有实时库存的购物车项目
要将事件数据(购物车内容)与自定义操作数据(库存状态)相结合,请查看以下示例。
| code language-handlebars |
|---|
|
示例:通过数据集查找扩充了事件数据
要将事件SKU与数据集查找中的详细产品信息相结合,请查看以下示例。
| code language-handlebars |
|---|
|
示例:将多个源与技术属性相结合
要将多个上下文源(用户档案数据、事件数据、自定义操作和技术属性)合并到一封邮件中,请查看以下示例。
| code language-handlebars |
|---|
|
其他上下文类型 other-contexts
虽然本指南侧重于数组上的迭代,但其他上下文类型可用于个性化,通常不需要迭代。 这些组件直接访问,而不是循环访问:
有关使用这些源的完整个性化语法和示例,请参阅:
在历程表达式中使用数组 arrays-in-journeys
虽然上一节侧重于使用Handlebars在消息个性化中迭代使用数组,但在配置历程活动时也可以使用数组。 本节介绍如何在历程表达式中使用来自事件的数组数据,尤其是将数据传递给自定义操作或使用具有数据集查找的数组时。
first、all和serializeList之类的函数一起使用。 在消息内容中,您将Handlebars语法与{{#each}}循环一起使用。将数组值传递到自定义操作参数 arrays-to-custom-actions
在配置自定义操作时,您通常需要从事件数组中提取值,并将其作为参数传递。 本节介绍常见模式。
了解有关在中将集合传递到自定义操作参数中的详细信息。
从数组中提取单个值
用例:从事件数组中获取特定字段,以作为查询参数传递到GET请求中。
示例场景:从产品列表中提取价格大于0的第一个SKU。
事件架构示例:
| code language-json |
|---|
|
自定义操作配置:
- 在自定义操作中,使用类型
string配置查询参数(例如sku) - 将其标记为
Variable以允许动态值
操作参数 中的 表达式历程:
| code language-javascript |
|---|
|
说明:
@event{YourEventName}:引用您的历程事件.first(currentEventField.condition):返回与条件匹配的第一个数组项currentEventField:代表事件数组中的每个项,因为您循环遍历它.SKU:从匹配项提取SKU字段- 结果:
"SKU-1"(适用于操作参数的字符串)
了解有关集合管理函数中的first函数的更多信息。
从数组构建值列表
用例:创建以逗号分隔的ID列表以作为查询参数传递(例如,/products?ids=sku1,sku2,sku3)。
自定义操作配置:
- 使用类型
string配置查询参数(例如ids) - 将其标记为
Variable
历程表达式:
| code language-javascript |
|---|
|
说明:
-
.all(currentEventField.condition):返回与条件匹配的所有数组项(返回列表) -
currentEventField:代表事件数组中的每个项,因为您循环遍历它 -
.SKU:对列表进行项目以仅包含SKU值 -
serializeList(list, delimiter, addQuotes):将列表加入字符串",":使用逗号作为分隔符true:在每个字符串元素周围添加引号
-
结果:
"SKU-1,SKU-3"(适用于查询参数)
详细了解:
将集合传递到自定义操作参数中涵盖了自定义操作的集合处理。
将对象数组传递给自定义操作
用例:发送请求正文中的对象的完整数组(对于POST或包含正文的GET)。
请求正文示例:
| code language-json |
|---|
|
自定义操作配置:
- 在请求正文中,将
products定义为类型listObject - 将其标记为
Variable - 定义对象字段:
id、name、price、color(每个字段都变为可映射)
历程画布配置:
-
在高级模式下,设置集合表达式:
code language-javascript @event{YourEventName.commerce.productListItems.all(currentEventField.priceTotal > 0)} -
在收藏集映射UI中:
- 将
id映射→productListItems.SKU - 将
name映射→productListItems.name - 将
price映射→productListItems.priceTotal - 将
color映射→productListItems.color
- 将
Journey Optimizer会构建与操作有效负载结构匹配的对象数组。
| note |
|---|
| NOTE |
使用事件数组时,使用currentEventField引用每个项。 对于数据源集合(Adobe Experience Platform),请使用currentDataPackField。 对于自定义操作响应集合,请使用currentActionField。 |
请参阅将集合传递到自定义操作参数以了解详情。
使用数组进行数据集查找 arrays-with-dataset-lookup
使用数据集查找活动时,您可以将一组值作为查找键传递以检索扩充数据。
示例:查找事件数组中所有SKU的产品详细信息。
数据集查找配置:
在查找键字段中,使用list()将数组路径转换为列表:
| code language-javascript |
|---|
|
这将创建一个列表,其中包含要在数据集中查找的所有SKU值。 结果在context.journey.datasetLookup.<activityID>.entities处可用作数组,您可以在消息中对其进行迭代(请参阅对数据集查找结果进行迭代)。
限制和模式 array-limitations
在历程中使用数组时,请注意以下限制:
在历程流中无需对阵列进行动态循环
历程无法创建动态循环,其中数组中的每个项会多次执行一个操作节点。 这是为了防止出现性能失控问题。
您无法执行的操作:
- 为每个数组项动态执行一次自定义操作
- 根据数组长度创建多个历程分支
推荐的模式:
在护栏和限制中了解详情。
阵列大小注意事项
大型阵列可能会影响历程性能:
- 事件数组:将事件有效负载保持在50 KB以下
- 自定义操作响应:响应负载应小于100KB
- 数据集查找结果:限制查找键和返回实体的数量
完整示例:从事件数组到自定义操作 complete-example
以下是一个完整的工作流,其中显示了如何将事件数组与自定义操作结合使用。
方案:当用户放弃购物车时,将购物车数据发送到外部推荐API以获取个性化建议,然后在电子邮件中显示这些建议。
步骤1:配置自定义操作
创建自定义操作“GetCartRecommendations”:
- 方法: POST
- URL:
https://api.example.com/recommendations - 请求正文:
| code language-json |
|---|
|
- 将
cartItems标记为类型listObject和Variable - 定义字段:
sku(字符串)、price(数字)、quantity(整数)
请参阅配置自定义操作以了解详情。
步骤2:配置响应有效负载
在自定义操作中,配置响应:
| code language-json |
|---|
|
在使用API调用响应中了解更多信息。
步骤3:在历程中引导操作
-
发生购物车放弃事件后,添加自定义操作
-
在
cartItems集合的高级模式下:code language-javascript @event{cartAbandonment.commerce.productListItems.all(currentEventField.quantity > 0)} -
映射收藏集字段:
sku→productListItems.SKUprice→productListItems.priceTotalquantity→productListItems.quantity
步骤4:在电子邮件中使用响应
在电子邮件内容中,对推荐进行迭代:
| code language-handlebars |
|---|
|
步骤5:测试您的配置
运行实时历程之前,使用操作配置中的“发送测试请求”功能测试自定义操作,以验证构建的请求和值。
请参阅自定义操作疑难解答以了解详情。
最佳实践 best-practices
在对上下文数据进行迭代以创建可维护、高性能个性化时,请遵循这些最佳实践。
使用描述性变量名称
选择明确指示您正在迭代的内容的变量名称。 这使得您的代码更易读取和维护。 详细了解个性化语法:
| code language-handlebars |
|---|
|
循环中的表达式片段
在{{#each}}循环中使用表达式片段时,请注意,不能将循环范围的变量作为片段参数传递。 但是,片段可以访问在片段之外的消息内容中定义的全局变量。
支持的模式 — 使用全局变量:
| code language-handlebars |
|---|
|
片段可以引用globalDiscount,因为它已在消息中全局定义。
不支持 — 传递循环变量:
| code language-handlebars |
|---|
|
解决方法:将个性化逻辑直接包含在循环中,而不是使用片段,或者在循环之外调用片段。
了解有关在循环中使用表达式片段的更多信息,包括详细示例和其他解决方法。
处理空数组
当数组为空时,使用{{else}}子句提供回退内容。 了解有关辅助函数的更多信息:
| code language-handlebars |
|---|
|
与条件帮助程序组合
在条件内容的循环中使用{{#if}}。 详细了解条件规则,并查看自定义操作响应和数据集查找部分中的示例。
| code language-handlebars |
|---|
|
限制性能迭代
对于大型数组,请考虑限制迭代次数:
| code language-handlebars |
|---|
|
访问阵列元数据
Handlebars在循环中提供特殊变量,帮助处理高级迭代模式:
@index:当前迭代索引(基于0)@first:第一个迭代为True@last:最后一个迭代为True
| code language-handlebars |
|---|
|
@index、@first、@last)仅在消息个性化的{{#each}}循环中可用。 若要在历程表达式中使用数组(例如,在传递到自定义操作之前从数组获取第一项),请使用数组函数,如head、first或all。 有关详细信息,请参阅在历程表达式中使用数组。疑难解答 troubleshooting
存在迭代问题? 本节介绍常见问题和解决方案。
数组未显示
问题:您的数组迭代未显示任何内容。
语法错误
问题:表达式验证失败或未呈现消息。
常见错误:
- 缺少结束标记:每个
{{#each}}都必须有一个{{/each}}。 请查看Handlebars迭代语法以了解正确结构。 - 变量名称不正确:请确保在整个块中一致使用变量名称。 有关命名惯例,请参阅最佳实践。
- 路径分隔符不正确:使用点(
.)而不使用斜线或其他字符
表达式片段无法在循环中使用
问题:表达式片段在{{#each}}循环中使用时不显示预期的内容,或显示空/意外的输出。
可能的原因和解决方案:
-
尝试将循环变量作为参数传递:表达式片段不能将循环范围的变量(如当前迭代项)作为参数接收。 这是已知的限制。
解决方案:使用以下变通方法之一:
- 在片段可以访问的消息中定义全局变量
- 将个性化逻辑直接包含在循环中,而不是使用片段
- 如果不需要特定于循环的数据,则调用循环外部的片段
-
片段需要不可用的参数:如果您的片段设计来接收特定的输入参数,那么当这些参数无法从循环中传递时,它将无法正常工作。
解决方案:重构您的方法,以使用片段可以访问的全局变量。 有关示例,请参阅最佳实践 — 循环中的表达式片段。
-
变量作用域不正确:片段可能正在尝试引用仅存在于循环作用域中的变量。
解决方案:定义片段在消息级别(循环外部)需要的任何变量,以便它们可全局访问。
了解有关在循环中使用表达式片段的更多信息,包括详细的说明、示例和推荐的模式。
测试迭代
使用历程测试模式验证您的迭代。 在使用自定义操作或数据集查找时,这一点尤其重要:
- 在测试模式下开始您的历程
- 使用示例数据触发事件或自定义操作
- 检查消息预览以验证迭代是否正确显示
- 查看测试模式日志中是否有任何错误(请参阅自定义操作测试模式日志)
相关主题 related-topics
Personalization基础知识: 个性化入门 | 添加个性化 | Personalization语法 | 辅助函数 | 创建条件规则
历程配置: 关于事件 | 配置自定义操作 | 将集合传递到自定义操作参数 | 在自定义操作中使用API调用响应 | 自定义操作疑难解答 | 在历程中使用Adobe Experience Platform数据 | 在历程中使用补充标识符 | 护栏和限制 | 测试您的历程
表达式函数历程: 高级表达式编辑器 | 集合管理函数(第一个、全部、最后一个) | 列出函数 (serializeList、筛选器、排序) | 数组函数 (头、尾)
Personalization使用案例: 购物车放弃电子邮件 | 订单状态通知
邮件设计:电子邮件设计入门 | 创建推送通知 | 创建短信消息 | 预览和测试您的内容
This section contains structured knowledge intended to support interpretation, retrieval, and question answering related to this topic.
For complete understanding, this information should be combined with the documentation on this page. Neither source is intended to stand alone; the page describes the feature, while this section provides additional context that helps disambiguate terminology, intent, applicability, and constraints.
- TL;DR: This page explains how to use Handlebars
{{#each}}syntax to loop over arrays from contextual sources — events, custom action responses, dataset lookups, and technical properties — in message personalization, and how to work with arrays in journey expression syntax when configuring journey activities.
Intents:
- Iterate over event array data (e.g., cart items, order items) in message personalization using
{{#each}} - Iterate over custom action response arrays (e.g., product recommendations) in messages
- Iterate over dataset lookup result arrays in messages
- Combine data from multiple contextual sources in a single personalized message
- Pass array values to custom action parameters using journey expression syntax
- Use arrays as lookup keys in Dataset Lookup activities
- Apply best practices for empty array fallbacks, variable naming, performance, and expression fragment scoping
Glossary:
- Handlebars: A templating language used in Journey Optimizer message personalization for iteration (
{{#each}}) and conditional rendering ({{#if}}). (product-specific) {{#each}}helper: Handlebars syntax for iterating over an array; each iteration exposes the current item via a named variable (e.g.,|product|). (product-specific)- Contextual data: Data available at message send time from journey sources — events, custom action responses, dataset lookups, and journey technical properties — as opposed to static profile attributes. (product-specific)
currentEventField: A reference used in journey expressions (not Handlebars) to refer to each item in an event array during filtering or mapping operations.currentActionField: Used in journey expressions to refer to each item in a custom action response collection.currentDataPackField: Used in journey expressions to refer to each item in a data source collection.serializeList: A journey expression function that converts a list of values into a delimited string (e.g., comma-separated), suitable for use as a query parameter.- Supplemental identifier: A journey-level identifier that distinguishes concurrent journey instances triggered by the same profile; used to filter an array to the item relevant to the current instance.
Guardrails:
- Journeys cannot create dynamic loops where one action node executes multiple times per array item — this is by design to prevent performance issues. Pass the entire array or a serialized list to a single custom action instead.
- Keep event payloads under 50KB total.
- Custom action response payloads should be under 100KB.
- Limit the number of dataset lookup keys and returned entities for performance.
- Expression fragments cannot receive loop-scoped variables (e.g., the current
{{#each}}iteration item) as parameters — this is a known limitation. Use global variables or inline logic instead. - Numeric event IDs must be wrapped in backticks in expression paths (e.g.,
context.journey.events.`1697323153`.fieldName); without backticks, the PQL parser raises a syntax error.
Terminology:
- Canonical name: Handlebars iteration — variants:
{{#each}}loop, each loop, array iteration - Do not confuse: Handlebars
{{#each}}syntax (used in message content for iteration and display) ≠ journey expression syntax (used in journey activity configuration — uses functions likefirst,all,serializeList) - Do not confuse:
currentEventField(journey expressions over event arrays) ≠currentActionField(custom action response collections) ≠currentDataPackField(data source collections) - Do not confuse:
@index/@first/@last(Handlebars special variables, only available within{{#each}}loops in message content) ≠first/headfunctions (journey expression functions for extracting single items, used in journey activity configuration)
FAQ:
- Q: What is the difference between Handlebars syntax and journey expression syntax when working with arrays? — Handlebars
{{#each}}is used in message content for iteration and display. Journey expression syntax — using functions likefirst,all, andserializeList— is used in journey activity configuration (e.g., custom action parameters, conditions). They are distinct syntaxes used in different contexts. - Q: Can I loop a journey action node so it executes once per array item? — No. Journeys cannot create dynamic loops that execute an action node multiple times per item. Instead, pass the entire array or a serialized list to a single custom action that processes all items, or use external aggregation.
- Q: Can I pass the current loop item to an expression fragment inside a
{{#each}}loop? — No. Expression fragments cannot receive loop-scoped variables as parameters. Use global variables defined outside the loop, or include the personalization logic directly within the loop instead of using a fragment. - Q: How do I display fallback content when an array is empty? — Use the
{{else}}clause within the{{#each}}block. Content inside{{else}}is rendered when the array has no items. - Q: What do
@index,@first, and@lastmean inside a{{#each}}loop? — These are special Handlebars variables available only within{{#each}}loops in message content:@indexis the 0-based current iteration index,@firstis true for the first iteration, and@lastis true for the last iteration.