写点什么

深入学习 SAP UI5 框架代码系列之六:SAP UI5 控件数据绑定的实现原理

作者:Jerry Wang
  • 2021 年 11 月 28 日
  • 本文字数:2392 字

    阅读完需:约 8 分钟

深入学习 SAP UI5 框架代码系列之六:SAP UI5 控件数据绑定的实现原理

在前端框架的学习过程中,控件数据绑定,一直是一个重点和难点。说它难,并不是控件绑定的语法有多难以掌握,而是指,当控件进行数据绑定后,运行时没有达到应用开发人员期望的效果后,应该如何着手分析问题症结所在。


之前 Jerry 时不时会在微信上收到一些朋友的咨询,诸如:“我的 SAP UI5 控件做了绑定,从 Chrome 开发者工具也能看到后台返回的 OData API 确实包含了数据,但是 UI5 页面渲染出来之后,为什么控件上没有显示绑定的数据呢?”此类问题。


其实,如果了解一些 SAP UI5 控件的数据绑定实现细节,此类问题完全可以通过自己调试的方式去排错。


我们在前一篇文章完成的应用里,添加下列五行高亮代码:


创建一个新的 JSON 模型,包含一个字段 field_for_text, 值为 Jerry,再将该模型绑定到 button 控件,模型字段 field_for_text 绑定到 button 的 text 属性。



这样一来,运行时看到按钮的标签就成了 field_for_text 的值"Jerry",而不是第 18 行给 text 属性赋予的硬编码值"Button":



本文的目的就是,阐述清楚第一张图高亮的五行代码,背后发生了什么事情。

var oModel = new sap.ui.model.json.JSONModel();


当这行代码执行之后,JSONModel 以及包含对应的模型绑定实现逻辑的 JavaScript 源文件会自动被加载,这体现了 SAP UI5 Module 的懒加载特性,在本系列这篇文章里介绍过:深入学习SAP UI5框架代码系列之一:UI5 Module的懒加载机制



本系列之前的文章,我们曾经多次提及 SAP UI5 控件的原型继承链:

Button -> Control -> Element -> ManagedObject

-> EventProvider -> BaseObject


本文我们会学习到另一条原型继承链:

JSONModel -> ClientModel -> Model -> MessageProcessor -> EventProvider -> BaseObject


从上图第 61 行的构造函数的输入参数 oData,不难发现,在 UI5 应用里创建 JSONModel 实例时,可以将模型存储的数据一并指定。


这样,我们之前脚手架应用里的代码,可以精简成下图右边所示:



单步调试 JSONModel 的构造函数,会发现它依次会调用原型继承链上游节点的 ClientModel,Model 和 MessageProcessor 的构造函数。


下图是 Model 构造函数的实现,信息量很大:


(1) 第 69 行,说明 SAP UI5 模型绑定的默认方式是双向绑定;


(2) 第 83 行,说明 SAP UI5 模型绑定支持单向,双向和单次绑定。本系列下一篇文章 SAP UI5 控件数据绑定的三种模式:One Way,Two Way 和 OneTime 实现原理比较 会介绍这三种方式的区别。


oModel.setData(myData);

将一个 JSON 对象通过 setData 传入 JSONModel 实例。



setData 方法有一个可选参数 bMerge,如果显式指定为 true,则会调用下图 88 行的 jQuery.extend, 将本地传入的 JSON 对象同 JSONModel 原有的数据进行合并(merge). 因为我们本例调用 setData 并未指定该参数,所以不进行合并,直接执行 95 行的 checkUpdate:



因为此时 UI5 button 控件实例尚未同 JSONModel 实例建立绑定关系,所以模型的 aBindings 数组为空,因此 checkUpdate 实际并未执行任何和控件绑定相关的操作,直接返回。


oButton1.setModel(oModel);

这行语句的作用,就是将控件实例和传入方法的 JSONModel 实例建立绑定关系。


  • 4597~4604:该 IF 分支处理 UI5 控件之前已经绑定到某个模型实例的情况,此时使用 delete 操作,删除原有的模型引用,然后更新绑定信息。而本例是 button 控件第一次调用 setModel,故不会进入此 IF 分支。



  • 4607:UI5 控件维护了一个类似字典的键值对数据结构,key 为模型名称,value 为模型实例。从此处的 SAP UI5 源代码实现不难得出结论,UI5 控件支持同时绑定到多个模型实例上,只要该控件在调用 setModel 方法时,给传入的模型实例通过方法第二个参数 sName,赋予不同的模型名称即可。



至此,JSONModel 实例的 field_for_text 字段值,还不会自动流向 button 控件的 text 属性,直到下面这行代码的执行。

oButton1.bindProperty(“text”, “/field_for_text”);

该方法首先构造了一个包含 JSONModel 绑定字段的对象 oBindingInfo:



将该 oBindingInfo 维护到控件的中央绑定信息存储数据结构 mBindingInfos 里,该数据结构同样是一个键值对,key 为控件属性,本例为 text,值为 oBindingInfo 对象,对象里包含了 text 属性绑定到 JSONModel 实例的字段名称:field_for_text.



单步调试进入上图 3347 行的_bindProperty 方法:


3417 行,调用 JSONModel 的 bindProperty 方法,生成 oBinding 对象:



这里再次出现了关于 SAP UI5 数据绑定的三种模式处理的源代码,本系列后续文章会专门介绍。


3433 行将生成的 oBinding 对象实例,添加到 aBindings 数组里。



那么 oBinding 实例是如何生成的?需要单步调试上图 3417 行的 oModel.bindProperty 代码。



JSONModel.bindProperty 的实现,就是一个单纯的 new 调用。不过更重要的是,此处我们了解到了另一条原型继承链:

JSONPropertyBinding -> ClientPropertyBinding -> PropertyBinding -> Binding -> EventProvider -> BaseObject


我们注意到,在 oBinding 实例里,有个字段叫做 oValue, 其值正好是 JSONModel 实例维护的 JSON 对象字段 field_for_text 的值 Jerry. 实际上,最后 button 控件的 text 属性,显示的值正是 oBinding 实例 oValue 字段的值。



所以需要搞清楚 this.oValue 的赋值逻辑,就得单步执行上图第 35 行的 this._getValue 方法。

this._getValue()

该函数负责将 field_for_text 字段的值,从 this.oModel.oData.field_for_text 字段中提取出来,如下图所示:



一旦 this._getValue 执行完之后,控件 text 属性同 JSONModel 实例的 field_for_text 字段就成功建立起绑定关系,之后我们就能直接从控件实例变量 oButton1 出发,去找到其 text 属性应该显示的值:



希望通过本文的介绍,能让大家对 SAP UI5 控件数据绑定的实现原理有一个最基本的了解。


如果遇到控件绑定不能按照自己的期望工作的时候,不妨试试按照本文提到的这些关键点去调试。


本系列下一篇文章,会介绍 SAP UI5 控件数据绑定的三种模式:One Way,Two Way 和 OneTime 的实现原理比较。


我的实际工作中发现,了解了 SAP UI5 这些数据绑定方式的实现,对我近期学习 Angular 的数据绑定也有一定的借鉴作用。


感谢阅读。



发布于: 3 小时前阅读数: 7
用户头像

Jerry Wang

关注

个人微信公众号:汪子熙 2017.12.03 加入

SAP成都研究院开发专家,SAP社区导师,SAP中国技术大使。

评论

发布
暂无评论
深入学习 SAP UI5 框架代码系列之六:SAP UI5 控件数据绑定的实现原理