SAP Fiori Elements List Report Smart Table 列项目宽度计算的奥妙
这是 Jerry 2021 年的第 36 篇文章,也是汪子熙公众号总共第 312 篇原创文章。
Jerry 之前写的 SAP Fiori Elements 系列文章:
关于 List Report 模板里 Smart Table 控件里不同列项目的宽度问题,也有很多朋友私下里问我:
比如这个问题:
“当我生成页面的时候,column 没有用光控件的空间,在传统开发上,我们可以操控 column 的 width 属性。但是在Fiori Elements 开发中并没有碰到相应的参数。请问您知道我可以通过什么参数控制嘛?”
因为我日常工作用的是 Angular 而非 SAP Fiori Elements,所以这些问题只有业余时间研究。此类问题我通常的做法是,在设法寻找解决方案之前 (如何通过代码调整 Smart Table 列项目的宽度),先做一些调研,搞清楚当前行为的实现原理。这样下次遇到新的列项目宽度的相关需求,之前调研学到的知识说不定能够重用。
Jerry 之前的文章 SAP Fiori Elements 框架里 Smart Table 控件的工作原理介绍,向大家揭秘了 Smart Table 控件里的列项目,来自 OData 元数据里拥有 com.sap.vocabiularies.UI.LineItem 注解的模型字段。但是当时我们没有关于列项目的宽度 (width) 进行讨论。
在 Chrome 开发者工具里,查看任意两个列项目的宽度,发现单位均为 rem:
px, em 和 rem 都是 css 的长度单位,其中 px 即像素,相对于显示器屏幕分辨率而言,在互联网早期应用里使用得比较多。而 em 和 rem 是相对长度单位,前者相对于其父元素,后者相对于文档根元素,在响应式页面设计里广泛使用。
通过 Jerry 之前这篇文章 Jerry的Fiori原创文章合集 里介绍的调试技巧,我成功找到了 Smart Table 运行时计算列项目宽度的代码位置。简言之,Fiori Elements List Report 模板里 Smart Table 每个列项目的初始宽度,同样取决于其 OData 元数据。
打开 Chrome 开发者工具,切换到 Sources 标签页,快捷键 Ctrl + O,在弹出对话框里输入 SmartTable,这样可以快速打开其实现文件 SmartTable.js:
该文件第 3817 行,_calcCoumnWidth 函数, 即 Smart Table 列项目宽度的计算实现源代码。
从源代码里的注释可知,Fiori Elements 基于 OData 元数据里的属性来计算列项目的宽度 (Calculates the column width from the metadata attributes).
The optimal column width is calculated with creating the longest possible sample of the created model type.
根据模型字段的类型,构造出该类型允许的最长内容的例子数据,即可计算出该列理论上的宽度。
计算列项目宽度函数 calcCoumnWidth 的输入参数:
oField:该列项目字段的 OData 元数据
bAdditionalProperty:boolean 类型,表明计算宽度时,除了字段 OData 元数据本身,是否还需要考虑其他因素
mConfig:计算宽度时使用的配置项,包含宽度的最大,最小值,默认值和其他配置标志位
这个函数实现代码里信息量很大:
计算宽度使用的配置对象,mConfig 的值,是在代码里以近似硬编码的方式填充的。从上图能够看出,Fiori Elements Smart Table 的列项目宽度,最大值为 19;如果计算时不考虑额外属性,最小宽度值为 2,否则为 1;如果 OData 元数据里提供的属性,不足以计算出一个合适的宽度值,则使用代码第 3822 行维护的默认宽度:在考虑额外属性情况下,默认宽度为 4,否则为 8;单位为 rem.
接下来为计算列项目宽度做准备。
第 3833 行的字符 w, 是为了针对字段元数据类型为 Edm.String, 即字符串类型的列项目做准备的。此类型字段,在元数据里还有另一个属性 MaxLength, 即字符串最大长度。
例如一个 MaxLength 为 40 的字符串类型字段,Smart Table 计算其宽度的逻辑就是,运行时生成一个由 40 个 w 字符组成的字符串,计算其渲染出来后占据的宽度,将该宽度值作为此列项目最终显示在界面上的宽度。
字段类型不同,其理论上最长的可能值也不同。比如上图第 3842 行开始的代码提到,类型 Edm.Int16, 理论的最长值为 32768, 占据 5 个字符的位置;Edm.Int32 则理论最多占据 9 个字符位,因为其最大值为 2147483648. 依次类推。
下图的 4 个 IF-ELSE 分支,分别对应着类型为 Byte, Time, Boolean 和 String 字段的宽度计算。下面我们来了解最简单的字符串类型的列项目宽度计算逻辑。
下图三个标号处的主要逻辑:字段 MainProductCategory, 类型为 Edm.String, 最大长度为 40 个字符,因此标号 3 处,将变量 sChar 代表的字符 w,重复 40 次后生成一个新的字符串,然后调用函数 measureText, 计算该字符串渲染出来的宽度。
在 measureText 函数内部,第 3792 行动态创建一个 canvas 标签,该标签常用于 JavaScript 动态绘制图形的场景。拿到 canvas 的 2d 上下文后,调用其原生 API,measureText,得到在该 canvas 上渲染长度为 40 的字符串需要占据的宽度,单位为 px,最后再除以 baseFontSize, 换算成 rem 单位。
计算出来的列项目宽度,会存储在 column 对象实例里,并在最终的 HTML 代码渲染过程里,交给 TableRenderer 进行 width 渲染。这就是我们在本文开始的 Chrome 开发者工具里,观察到 HTML 源代码里 th 元素的 CSS style 面板里的 width 属性。
至此,本文介绍了 SAP Fiori Elements List Report Smart Table 列项目的初始宽度计算逻辑。从以上逻辑大家不难发现,宽度计算是根据字段元数据的 MaxLength 属性,而不是运行时该字段实际的显示值,因而会出现某些朋友抱怨的 “虽然某列显示的内容很少,但还是占据了很宽的空间,导致屏幕空间利用率不够" 的问题, 如下图所示:
另外,我们点击 Settings 按钮进行个性化设置:
让 Smart Table 运行时只显示两列:
之后显示的页面,确实只包含我们在个性化设置里选择的 Image 和 Product 两列,然而它们的宽度,和个性化之前相比并未发生变化,因此页面非常难看:
Jerry 后续的 SAP Fiori Elements 系列文章,会介绍如何解决这个宽度没能够动态更新的问题,敬请期待。
更多阅读