这是 Jerry 2021 年的第 34 篇文章,也是汪子熙公众号总共第 310 篇原创文章。
Jerry 前一篇文章 深入掌握 SAP Fiori Elements 工作原理系列之二:如何给 Fiori Elements 应用添加自定义按钮 介绍了如何给 SAP Fiori Elements 应用的 Smart Table 工具栏里,新增自定义按钮,并实现其点击事件处理函数。
本文 Jerry 继续介绍 SAP Fiori Elements 应用里,Smart Table 控件的渲染原理。根据前一篇文章介绍的知识,SAP Fiori Elements List Report 的模板,包含了 SmartTable.fragment.xml 这个页面片段:
而该页面片段的源代码里,使用了 Smart Table 控件:
为了把和 Smart Table 控件不相关的依赖都剥离开,以便于大家把注意力聚焦在 Smart Table 本身上,本文 Jerry 另外开发了一个 SAP UI5 应用,只包含一个最简单的 XML 视图,里面使用到了 Smart Table 控件。同时,我开发了一个简单的 Mock 服务器,让该应用在请求 OData 服务元数据时,由 Mock 服务器把本地工程事先准备好的 metadata.xml 文件的内容,作为元数据响应,直接返回给应用。这样我可以方便地修改本地 metadata.xml 文件,来达到各种测试目的。
关于如何在 SAP UI5 应用里手动搭建 Mock 服务器,请参考我这篇文章:何以 mock server 的方式本地启动 SAP UI5 应用,使它不连接远程服务器端的 OData 服务.
本文这个用于演示 Smart Table 控件使用方法的 SAP UI5 应用,其完整源代码在我的 Github 上。
该应用三个核心文件:
-
metadata.xml: 该文件的内容即 OData 服务的元数据,包含加上了 SAP Fiori Elements 系列的自定义注解,我们稍后会详细研究该文件内容。当 SAP UI5 应用向服务器发起 OData 服务元数据请求时,该请求被 Mock 服务器拦截,后者将 metadata.xml 的内容作为 OData 服务元数据响应,返回给应用。
-
Products.json: 测试数据 (Mock Data). 当 SAP UI5 应用准备渲染 Smart Table 待显示的业务数据时,会向远端服务器发起 OData batch 请求。该请求同样会被 Mock 服务器拦截,并将该 Products.json 的内容返回给应用。
-
server.js: Jerry 开发的 Mock 服务器实现。
XML 视图里定义了一个 Smart Table 控件,第 10 行代码 entitySet="Products", 意思是让该控件,在运行时"智能地" 将名称为 Products 的 OData 模型里,所有符合某种条件的字段,渲染成表格列项目。
这个包含了 Smart Table 控件的 SAP UI5 应用,最终渲染成包含如下三列的表格:产品 ID,价格 (含金额和货币单位) 以及产品名称。
我们打开 metadata.xml, 找到了 Product 模型包含的四个属性字段。这四个属性字段,都作为最后渲染出的列项目的备选字段。其中 Price 字段,通过属性 sap:unit, 和 CurrencyCode 字段关联起来,作为同一个表格备选列项目,其工作原理在 Jerry 之前的文章 深入掌握 SAP Fiori Elements 工作原理的前提条件:理解 Smart Field 里介绍过。
尽管 Product 模型包含了 4 个字段作为表格备选列项目,但为什么最终渲染出的页面里,我们只看到了 3 个行项目?名为 Category 的字段为什么没能渲染成行项目?
答案在 metadata.xml 的注解区域。
SAP 帮助文档提到,其所属的 OData 模型被注解 com.sap.vocabiularies.UI.LineItem 修饰,且类型为 com.sap.vocabularies.UI.DataField 的字段,在运行时会被 SAP UI5 框架绘制成表格列项目。
为了验证这个结论,我们对 metadata.xml 里的元数据进行一些修改。比如现在只定义两个表格列项目,分别为ProductId 和 Name. 同时,我用 sap:label, 给属性 ProductId 分配标签为 "Jerry产品ID":
运行时的效果:Name 列表项出现在 ProductId 的左边,因为其在元数据里的定义,位置在 ProductId 之前。
至此我们已经了解了 Smart Table 表格列项目渲染的逻辑,最后来看看源代码实现。
- 我的 UI5 应用里,使用了 Smart Table 控件的 XML 视图,运行时被加载后,会被 SAP UI5 的 XML 模板解析器, XMLTemplateProcessor 的方法 parseTemplate 所解析。XML 视图包含的 XML 字符串,会被反序列化成 DOM 并进行遍历。当模板解析器遍历 DOM 过程中,遇到 SmartTable 标签时,调用 SmartTable.init 方法,进行初始化操作:
- 根据本文前半部分的介绍,我们已经知道:如果缺乏 OData 元数据提供的注解,Smart Table 控件无法知道该怎么渲染表格的列项目。因此,SmartTable.js 也在 "OData 服务元数据成功取回" 这个事件上,注册了一个钩子函数 _onMetadataInitialised. 当 OData 服务的元数据取回之后,该回调函数被调用:
在该回调函数执行的上下文里,因为 OData 服务元数据已经处于可访问状态,所以 Smart Table 有足够的信息,能够开始渲染逻辑的执行:
下图第 97 行的高亮代码,getLineItemAnnotation, 即是 Smart Table 控件,准备从 Product 这个 EntityType 里,解析出符合表格列项目渲染要求的字段列表:
注意下图第 1909 行硬编码的字符串 com.sap.vocabularies.UI.v1.LineItem, 这就是 UI5 框架代码里查找 Smart Table 待渲染列表项字段的依据。最后解析出的两个列表项字段,Name 和 ProductId,就存储在函数返回变量 oResolvedAnnotation.
有了这个信息,Smart Table 就知道该渲染哪些字段作为表格列项目了。
至此,本文已经完成了 Smart Table 控件渲染表格列项目的原理介绍,以及相应的 SAP UI5 框架是如何解析待渲染列项目的源代码实现的介绍。
七年前,Jerry 刚刚从 ABAP 开发转到 SAP UI5 开发时,对本文介绍的这些注解概念,理解得似是而非,因为之前用的 ABAP 这门编程语言,无法像 Java 和 TypeScript 那样,能够从语言层面提供对注解的原生支持。
后来接触了 Java Spring 框架,再加上最近使用 Angular 做开发后,对注解的理解也比之前单纯阅读 SAP 文档要深入一些了。在 Java,Angular 和 SAP Fiori Elements 里,虽然这些注解的语法有差异,但目的都一致,即提供一种对注解的目标对象,进行额外数据标注的功能。
比如 Component 是 Angular UI 最基本的组成单元,而 Component 的定义,无非就是普通的 TypeScript class,加上 @Component 注解的修饰而成。该注解能维护 Component 的元数据,告诉 Angular 框架,该 Component 在运行时应该如何被实例化和使用。
希望本文能帮助大家更好地理解 SAP 帮助文档上对 SAP Fiori Elements 相关 OData 注解的介绍内容。感谢阅读。
更多阅读
更多Jerry的原创文章,尽在:"汪子熙":