深入学习 SAP UI5 框架代码系列之七:控件数据绑定的三种模式 - One Way, Two Way 和 OneTime 实现原理比较

如果是从 Angular 前端开发转过来的 SAP UI5 应用开发人员,想必对 Angular 的 Property binding,Event binding 和 Two-way binding 的实现原理和区别已经有所了解了。

Angular 这三种绑定方式的使用语法如下图所示:

但咱们本文不会阐述 Angular 的数据绑定细节,而是继续聚焦在 SAP UI5 的数据绑定三种模式的探讨上。
Jerry 前一篇文章 深入学习SAP UI5框架代码系列之六:SAP UI5控件数据绑定的实现原理,曾经展示过这张源代码截图,第 83 行包含了 SAP UI5 支持的三种数据绑定模式:
(1) OneWay:单向绑定(2) TwoWay:双向绑定(3) OneTime:单次绑定

从第 69 行代码得知,SAP UI5 模型的默认绑定方式为双向绑定 TwoWay.
当我第一次了解了 SAP UI5 三种不同的数据绑定模式时,脑子里马上浮现出一个问题:
在我们的脚手架应用里,将 button 控件的 text 属性,通过 bindProperty 函数,绑定到 JSONModel 实例的 field_for_text 字段上时,使用的是哪一种绑定模式?
答案是 TwoWay,双向绑定。在调试器里,通过下图路径即可查看:

方法 bindProperty 里创建 oBinding 对象时,把 JSONModel 的默认 binding 方式(sDefaultBindingMode), TwoWay, 赋给 oBinding 对象。

SAP UI5 双向绑定的实现原理 - 数据从控件流向模型
我们调用控件的 setText 方法,修改控件 text 属性。根据 SAP UI5 数据双向绑定特性,控件 text 属性绑定到的模型字段 field_for_text 的值,也会自动被修改:

从上图我们能确认,JSONModel 模型实例的 field_for_text 也跟着被修改了。这一切是怎么发生的?
答案在 Jerry 之前的文章 深入学习SAP UI5框架代码系列之五:SAP UI5控件的实例数据修改和读取逻辑 介绍的 button 控件的 setText 方法里,其实已经提到过。
下图第 1320 行的代码,执行的逻辑正是在控件属性通过 setProperty 被更新的场景下,将最新值同步到对应的模型字段中去。

在 updateModelProperty 函数内部,有一个 IF 条件判断:只有当前 oBinding 对象实例的绑定模式为 TwoWay 时,才调用其 setExternalValue 方法,将模型字段的对应值,修改成来自控件属性通过 setText 更改的最新值。
这就是控件 text 属性的变化,能传递到对应模型字段的原理。

SAP UI5 单向绑定的工作原理
通过之前的介绍我们得知,SAP UI5 控件绑定的默认模式为 TwoWay. 因此,我们如果要测试单向绑定,需要对已有的代码进行修改,将绑定模式显式修改成 OneWay:
此时,控件 text 属性通过 setText 的修改,不会再触发 JSONModel 模型字段的变化,因为下图 3620 行 IF 语句的条件不再成立。这就是 SAP UI5 单向数据绑定的原理:数据仅仅会在模型字段到控件属性这个方向上单向流动。JSONModel 字段值发生变化后,控件对应属性会自动更新。反之,控件属性通过 API 被修改时,不会引起 JSONModel 字段值的更新。

SAP UI5 单次绑定的工作原理
SAP UI5 官方网站上对单次绑定(OneTime)的说明:value is only read from the model once.
怎么理解这句话呢?

通过一个实际的例子来理解双向绑定和单次绑定的区别。
在双向绑定的测试代码里,添加如下两行代码:

模型字段 field_for_text 的初始值,在第 28 行赋值为 Jerry, 然后在第 34 行设置为 Tom. 调用模型的 checkUpdate 方法后,控件的标签也自动刷新为 Tom.
JSONModel 的 checkUpdate 方法,会使用_fireChange,以事件通知的方式,将最新的 Tom 值广播出去。

that.updateProperty 最后会调用 SAP UI5 控件的 setProperty 方法,把 text 属性的值赋成 Tom.
下图展示的 setProperty 方法,在 Jerry 之前的文章 深入学习SAP UI5框架代码系列之五:SAP UI5控件的实例数据修改和读取逻辑 里有详细介绍。

现在开始测试单次绑定,将 32 行 bindProperty 函数调用里的绑定模式改成 OneTime:

测试发现,在单次绑定模式下,SAP UI5 控件属性和模型字段的自动同步已经中断了——button 控件的 text 属性,保存的是调用 bindProperty 方法那一刻,JSONModel 的 field_for_text 值 Jerry,而不是数据绑定之后,修改成的最新值 Tom.
这个行为正是 SAP UI5 单次绑定的正确表现。

那么为什么在单次绑定模式下,纵然我们调用了模型的 checkUpdate 方法,仍然无法将模型字段的最新值,通过 change 事件通知机制告诉给控件的监听函数呢?
答案就是,在 SAP UI5 控件指定了单次绑定的模式后,它"过河拆桥",马上就把响应模型 change 事件的监听函数拆除了(detach,取消注册之意)。

奥妙就在下图 3379 行代码的 IF 分支里:在 SAP UI5 控件调用 bindProperty 方法把控件属性绑定到模型字段时,如果当前绑定模式为 OneTime,则取消控件针对模型 change 事件的监听函数注册。
如此一来,不论接下来模型字段的值如何变化,该变化的值通过 change 事件进行广播,但 UI5 控件再也不会收到该事件了,因而控件属性也不会再刷新。

Jerry 在 SAP UI5 / Angular 的实际开发生涯中,一旦遇到数据绑定出问题,总能迅速找到如何排错的突破口,靠的就是对这些前端框架的数据绑定细节的熟悉。
希望本文能帮助大家对 SAP UI5 官方网站上解释数据绑定三种方式的说明文字,有更深入的理解,感谢阅读。

本系列下一篇文章,我们会介绍 SAP UI5 控件 ID 的生成逻辑,敬请期待。
版权声明: 本文为 InfoQ 作者【Jerry Wang】的原创文章。
原文链接:【http://xie.infoq.cn/article/1b443332cd0862517beaacc7d】。
本文遵守【CC-BY 4.0】协议,转载请保留原文出处及本版权声明。
评论