笔者已经在一款名叫 Spartacus 的开源 Commerce Storefront 项目中工作了三年多的时间,这个项目的技术栈是 Angular,本文分享笔者在这三年 Angular 开发里,关于这个项目里路由路径(Router path)的自定义配置和开发领域学习到的一些经验。
从产品列表页面跳转到产品明细页面,是通过 Angular 标准的 Router 组件实现的。
下图产品明细页面的 url:
http://localhost:4202/electronics-spa/en/USD/product/358639/D...
很明显,这个产品明细页面的 url,由下列三个片段拼接而成:
- 静态的字符串 product
- 动态参数 358639, 即产品 ID
- 动态参数 DSC-N1,即产品名称
产品 ID 和产品名称维护在 SAP Commerce Cloud Backoffice 里:
SAP Commerce Cloud UI 的页面默认路由即 url 格式,通过 Spartacus 接口 RoutesConfig 来定义:
/*
* SPDX-FileCopyrightText: 2023 SAP Spartacus team <spartacus-team@sap.com>
*
* SPDX-License-Identifier: Apache-2.0
*/
import { RoutesConfig, RoutingConfig } from '@spartacus/core';
export const defaultStorefrontRoutesConfig: RoutesConfig = {
home: { paths: [''] },
notFound: { paths: ['not-found'] },
// semantic links for login related pages
login: {
paths: ['login'],
protected: false,
authFlow: true,
},
register: {
paths: ['login/register'],
protected: false,
authFlow: true,
},
forgotPassword: {
paths: ['login/forgot-password'],
protected: false,
authFlow: true,
},
resetPassword: {
paths: ['login/pw/change'],
protected: false,
authFlow: true,
},
logout: { paths: ['logout'], protected: false, authFlow: true },
// plp routes
search: { paths: ['search/:query'] },
category: {
paths: ['category/:categoryCode'],
paramsMapping: { categoryCode: 'code' },
},
brand: { paths: ['Brands/:brandName/c/:brandCode'] },
// pdp routes
product: {
paths: ['product/:productCode/:name'],
paramsMapping: { productCode: 'code' },
},
termsAndConditions: { paths: ['terms-and-conditions'] },
coupons: { paths: ['my-account/coupons'] },
couponClaim: {
paths: ['my-account/coupon/claim/:couponCode'],
paramsMapping: { couponCode: 'code' },
},
myInterests: {
paths: ['my-account/my-interests'],
},
notificationPreference: {
paths: ['my-account/notification-preference'],
},
};
export const defaultRoutingConfig: RoutingConfig = {
routing: {
routes: defaultStorefrontRoutesConfig,
},
};
上图第35行的 paths 数组里包含的内容,即产品明细页面 ( 我们开发团队习惯简称为 PDP - Product Detail Page ) 的路由 url 模板。其中 :productCode 是一个占位符,运行时会被 36 行指定的参数映射数组( paramsMapping )里指定的 code,即 SAP Commerce Cloud 后台 Product 模型里的 code 字段的值取代。
之所以引入参数映射的机制,而不直接将 Product 模型字段 code 加入到 url 模板里,是为了让 url 模板语义上更加清晰,达到自描述的效果。因为 code 这个单词比较通用,到底指 Product code,还是指 Product category code 呢?而在 url 模板里使用表述更加精确的 productCode,再将其映射到 Product 模型的 code 字段,这样避免了可能产生的歧义。
当我们另外打开一个名称比较长的产品时,发现此时 url 的显示效果有些不尽如人意——产品名称中的空格,被 encode 处理成了 %20:
http://localhost:4202/electronics-spa/en/USD/product/23355/Fl...
因此本文借这个例子,介绍如何通过简单的二次开发,来更改 SAP Commerce Cloud ( 电商云 ) 里产品明细页面 url 的显示格式。
假设我们需要实现如下需求:
将产品名称中包含的空格,替换成连接号 “-” , 并显示在 url 里。
在 url 里显示产品的制造商信息,即 Product 模型的 manufacturer 字段。
具体实现步骤如下:
- 新建一个 Angular module,使用 Spartacus 提供的工具方法,ConfigModule.withConfig, 定义一条新的路由配置,键为 product,其值会覆盖掉 Spartacus 发布的同名默认路由配置:
第31行的 url 模板,包含了静态字符串 manufacturer, 其后紧跟 Product 模型上的同名字段,表明该产品的制造商名称,这样就实现了需求2.
而对于需求1即产品名称的空格替换为"-", 最常规的思路是,在产品数据从 SAP Commerce Cloud 后台返回前台之后,找一个可以编写自定义逻辑的钩子函数 ( hook ) 进行实现。
从 Commerce 系统读取产品主数据,读取的字段列表以 url 参数的形式出现在 API endpoint里。这些字段列表可以在 Connector 的静态配置点里进行配置:
Connector 并不会直接同 Commerce 交互,而是把请求转发给 Adapter,具体通信由 Adapter 完成,Connector 只负责调度 Adapter.
Connector 将 Adapter 取回的数据交给 NgRx 的 store 结构统一管理,后者的复杂度被 Facade 层所隐藏,而 Spartacus UI 组件只会同 Facade 层交互,进行数据绑定和页面展示。这体现了关注点分离的设计原则。因为 Commerce UI 组件和 Commerce 后台组件的数据模型存在差异,因此需要 Converter ( 有时也称 Normalizer ),在数据从 Commerce 取回,准备呈现在 UI 之前,先要通过 Converter 转换成适合 UI 展示的结构。
因此我创建了一个 ProductNameNormalizer,继承自Converter,实现其 convert 方法,在里面将 Product 模型的 name 字段,用正则表达式将包含的所有空格字符,替换成 “-”.
上图第9行,我没有将替换后的结果值,存储到 Product 模型的 name 字段里,而是引入了一个新的 nameForUrl 字段,以避免影响其他可能同样基于该 name 字段的二次开发。
这个新引入的仅仅用于展示在浏览器地址栏里的字段,在很多 SAP 产品里都有广泛的应用,称呼也各不相同。然而它们都有着同样的特征:
- 没有后台持久化存储,纯粹用于UI显示
- 为了弥补后台模型和 UI 模型字段差异而引入
- 其值通常都是动态计算而成
典型的一个例子是,后台存储了员工的出生年月,而 UI 显示的字段是年龄,那么 UI 模型里引入一个年龄字段,其值为当前年月减去员工出生年月。
在 SAP CRM 里,这种运行时根据某种规则,动态填充其值,供 UI 显示的字段,叫做 Calculated Field:
在 SAP CRM AET (Application Extension Tool) 里,Key User 可以通过简单的规则编辑器,维护 Calculated Field 的计算逻辑。当然,这些规则保存后,会编译生成对应的 ABAP 代码。
而到了 SAP Cloud for Customer 里,除了同样支持 SAP CRM Calculated Field 的规则编辑功能之外,还提供了两种允许二次开发人员,通过编程来填充用于 UI 临时显示字段值的方式:
- Transient Field
在 SAP Cloud for Customer BO 定义代码里,使用注解 Transient,将一个字段标注成 Transient 字段:
然后其值通过编写 AfterLoading 这个 hook 来填充。
- Dedicated Field
在 SAP C4C UI Designer 里将字段类型设置为 Dedicated Field,然后给其分配一个 Transformation,该 Transformation 由二次开发人员用 ABSL ( ABAP Scripting Language ) 实现,负责计算 Dedicated Field 的值。
回到本文介绍的 SAP Commerce Cloud UI 二次开发的例子。我的 Calculated Field,nameForUrl 的值,直接通过 ProductNameNormalizer 实现的 convert 方法填充。
至此这个需求的开发步骤已经结束。最后,将存放了把空格替换成 “-” 的 nameForUrl 字段(下图绿色高亮),映射到产品明细页面 url 模板 (下图红色高亮) 即可。
最后的效果:
现在的 url:
http://localhost:4204/electronics-spa/en/USD/manufacturer/Son...
是不是比之前的 url,可读性要好一些?
http://localhost:4202/electronics-spa/en/USD/product/23355/Fl...
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。