浅谈 Angular 应用里路由路径的自定义配置与开发

宣高
• 阅读 588

笔者已经在一款名叫 Spartacus 的开源 Commerce Storefront 项目中工作了三年多的时间,这个项目的技术栈是 Angular,本文分享笔者在这三年 Angular 开发里,关于这个项目里路由路径(Router path)的自定义配置和开发领域学习到的一些经验。

从产品列表页面跳转到产品明细页面,是通过 Angular 标准的 Router 组件实现的。

浅谈 Angular 应用里路由路径的自定义配置与开发

下图产品明细页面的 url:

http://localhost:4202/electronics-spa/en/USD/product/358639/D...

浅谈 Angular 应用里路由路径的自定义配置与开发

很明显,这个产品明细页面的 url,由下列三个片段拼接而成:

  1. 静态的字符串 product
  2. 动态参数 358639, 即产品 ID
  3. 动态参数 DSC-N1,即产品名称

浅谈 Angular 应用里路由路径的自定义配置与开发

产品 ID 和产品名称维护在 SAP Commerce Cloud Backoffice 里:

浅谈 Angular 应用里路由路径的自定义配置与开发

SAP Commerce Cloud UI 的页面默认路由即 url 格式,通过 Spartacus 接口 RoutesConfig 来定义:

浅谈 Angular 应用里路由路径的自定义配置与开发

/*
 * 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:

浅谈 Angular 应用里路由路径的自定义配置与开发

http://localhost:4202/electronics-spa/en/USD/product/23355/Fl...

因此本文借这个例子,介绍如何通过简单的二次开发,来更改 SAP Commerce Cloud ( 电商云 ) 里产品明细页面 url 的显示格式。

假设我们需要实现如下需求:

将产品名称中包含的空格,替换成连接号 “-” , 并显示在 url 里。
在 url 里显示产品的制造商信息,即 Product 模型的 manufacturer 字段。

具体实现步骤如下:

  1. 新建一个 Angular module,使用 Spartacus 提供的工具方法,ConfigModule.withConfig, 定义一条新的路由配置,键为 product,其值会覆盖掉 Spartacus 发布的同名默认路由配置:

浅谈 Angular 应用里路由路径的自定义配置与开发

第31行的 url 模板,包含了静态字符串 manufacturer, 其后紧跟 Product 模型上的同名字段,表明该产品的制造商名称,这样就实现了需求2.

而对于需求1即产品名称的空格替换为"-", 最常规的思路是,在产品数据从 SAP Commerce Cloud 后台返回前台之后,找一个可以编写自定义逻辑的钩子函数 ( hook ) 进行实现。

浅谈 Angular 应用里路由路径的自定义配置与开发

从 Commerce 系统读取产品主数据,读取的字段列表以 url 参数的形式出现在 API endpoint里。这些字段列表可以在 Connector 的静态配置点里进行配置:

浅谈 Angular 应用里路由路径的自定义配置与开发

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 字段,用正则表达式将包含的所有空格字符,替换成 “-”.

浅谈 Angular 应用里路由路径的自定义配置与开发

上图第9行,我没有将替换后的结果值,存储到 Product 模型的 name 字段里,而是引入了一个新的 nameForUrl 字段,以避免影响其他可能同样基于该 name 字段的二次开发。

这个新引入的仅仅用于展示在浏览器地址栏里的字段,在很多 SAP 产品里都有广泛的应用,称呼也各不相同。然而它们都有着同样的特征:

  • 没有后台持久化存储,纯粹用于UI显示
  • 为了弥补后台模型和 UI 模型字段差异而引入
  • 其值通常都是动态计算而成

典型的一个例子是,后台存储了员工的出生年月,而 UI 显示的字段是年龄,那么 UI 模型里引入一个年龄字段,其值为当前年月减去员工出生年月。

在 SAP CRM 里,这种运行时根据某种规则,动态填充其值,供 UI 显示的字段,叫做 Calculated Field:

浅谈 Angular 应用里路由路径的自定义配置与开发

在 SAP CRM AET (Application Extension Tool) 里,Key User 可以通过简单的规则编辑器,维护 Calculated Field 的计算逻辑。当然,这些规则保存后,会编译生成对应的 ABAP 代码。

浅谈 Angular 应用里路由路径的自定义配置与开发

而到了 SAP Cloud for Customer 里,除了同样支持 SAP CRM Calculated Field 的规则编辑功能之外,还提供了两种允许二次开发人员,通过编程来填充用于 UI 临时显示字段值的方式:

  1. Transient Field

在 SAP Cloud for Customer BO 定义代码里,使用注解 Transient,将一个字段标注成 Transient 字段:

浅谈 Angular 应用里路由路径的自定义配置与开发

然后其值通过编写 AfterLoading 这个 hook 来填充。

  1. 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 模板 (下图红色高亮) 即可。

浅谈 Angular 应用里路由路径的自定义配置与开发

最后的效果:

浅谈 Angular 应用里路由路径的自定义配置与开发

现在的 url:

http://localhost:4204/electronics-spa/en/USD/manufacturer/Son...

是不是比之前的 url,可读性要好一些?

http://localhost:4202/electronics-spa/en/USD/product/23355/Fl...

点赞
收藏
评论区
推荐文章
blmius blmius
4年前
MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1
文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s
Wesley13 Wesley13
3年前
MySQL部分从库上面因为大量的临时表tmp_table造成慢查询
背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_
美凌格栋栋酱 美凌格栋栋酱
7个月前
Oracle 分组与拼接字符串同时使用
SELECTT.,ROWNUMIDFROM(SELECTT.EMPLID,T.NAME,T.BU,T.REALDEPART,T.FORMATDATE,SUM(T.S0)S0,MAX(UPDATETIME)CREATETIME,LISTAGG(TOCHAR(
皕杰报表之UUID
​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为
Jacquelyn38 Jacquelyn38
4年前
2020年前端实用代码段,为你的工作保驾护航
有空的时候,自己总结了几个代码段,在开发中也经常使用,谢谢。1、使用解构获取json数据let jsonData  id: 1,status: "OK",data: 'a', 'b';let  id, status, data: number   jsonData;console.log(id, status, number )
Wesley13 Wesley13
3年前
FLV文件格式
1.        FLV文件对齐方式FLV文件以大端对齐方式存放多字节整型。如存放数字无符号16位的数字300(0x012C),那么在FLV文件中存放的顺序是:|0x01|0x2C|。如果是无符号32位数字300(0x0000012C),那么在FLV文件中的存放顺序是:|0x00|0x00|0x00|0x01|0x2C。2.  
Wesley13 Wesley13
3年前
mysql设置时区
mysql设置时区mysql\_query("SETtime\_zone'8:00'")ordie('时区设置失败,请联系管理员!');中国在东8区所以加8方法二:selectcount(user\_id)asdevice,CONVERT\_TZ(FROM\_UNIXTIME(reg\_time),'08:00','0
Wesley13 Wesley13
3年前
PHP创建多级树型结构
<!lang:php<?php$areaarray(array('id'1,'pid'0,'name''中国'),array('id'5,'pid'0,'name''美国'),array('id'2,'pid'1,'name''吉林'),array('id'4,'pid'2,'n
Wesley13 Wesley13
3年前
Java日期时间API系列36
  十二时辰,古代劳动人民把一昼夜划分成十二个时段,每一个时段叫一个时辰。二十四小时和十二时辰对照表:时辰时间24时制子时深夜11:00凌晨01:0023:0001:00丑时上午01:00上午03:0001:0003:00寅时上午03:00上午0
Stella981 Stella981
3年前
Django中Admin中的一些参数配置
设置在列表中显示的字段,id为django模型默认的主键list_display('id','name','sex','profession','email','qq','phone','status','create_time')设置在列表可编辑字段list_editable
Python进阶者 Python进阶者
1年前
Excel中这日期老是出来00:00:00,怎么用Pandas把这个去除
大家好,我是皮皮。一、前言前几天在Python白银交流群【上海新年人】问了一个Pandas数据筛选的问题。问题如下:这日期老是出来00:00:00,怎么把这个去除。二、实现过程后来【论草莓如何成为冻干莓】给了一个思路和代码如下:pd.toexcel之前把这