关于 javascript:如何在 UI5 中正确附加和分离事件处理程序

How to properly attach and detach event handler in UI5

我的自定义控件的数据绑定有问题。

我的控件继承自 sap.m.Input 并使用特殊的值助手对其进行扩展。我的新控件的新属性之一是值帮助对话框的简单标题。这绑定到 i18n 模型。

当我现在以正常形式使用我的控件时,一切正常。标题已正确绑定,并显示了该模型中绑定的 i18n 属性的值。如果我在 sap.ui.table 控件的列中使用我的控件作为模板,它只显示 title 属性的默认值。数据绑定似乎不起作用。但仍在处理继承的属性(例如值)。

为了简单起见,我的控件现在只有 title 属性,如果请求值帮助,它会在警告框中显示当前值。在表中,它显示了默认值。并且没有表格,它显示了 i18n 模型的绑定值。

这里是简化的控制代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
sap.ui.define([
 "sap/ui/core/Control",
 "sap/m/Input",
], function(Control, Input) {
 "use strict";

  return Input.extend("DvpClsSuggestInput", {
   "metadata": {
       "properties": {
          // Title of Value-Help Dialog
         "vhTitle": {
            type:"string",
            defaultValue:"Title"
          }
        }
      },
   
      init: function() {
        Input.prototype.init.apply(this, arguments);
        this.setShowValueHelp(true);
        this.attachValueHelpRequest(this.onValueHelpRequest.bind(this));
      },
   
      onValueHelpRequest: function(oEvent) {
        var lvTitle = this.getVhTitle();
        alert(lvTitle);
      },

    });
  });
});

sap.ui.table.Table 中的用法(不起作用并显示 title 属性的默认值):

1
2
3
4
5
6
7
8
<table:Column>
  <m:Label text="{i18gn>HausWaehrung}" />
  <table:template>
    <dvp:MyInput
      value="{ path: 'Inv>Hwaer', type: 'sap.ui.model.type.String' }"
      vhTitle="{i18n>Currency}" />
  </table:template>
</table:column>

有效的用法:

1
2
3
4
5
<VBox>
  <dvp:MyInput
    value="{ path: 'Cls>/Currency', type: 'sap.ui.model.type.String' }"
    vhTitle="{i18n>Currency}" />
</VBox>

再一次,对 value 属性的绑定有两种方式。问题只存在于我自己的属性 vhTitle 中。欢迎任何想法。


在将事件处理程序附加到 ManagedObject\\ 的事件时不要使用 .bind。这同样适用于分离事件处理程序。 UI5 有它自己的记录机制,用于为这些情况传递侦听器对象。

示例 1

使用相应的 API 附加/分离 valueHelpRequest 处理程序并将值传递到 API 参考中记录的参数列表:

1
2
myInput.attachValueHelpRequest(/*obj?,*/this.onValueHelpRequest, this); // No .bind!
myInput.detachValueHelpRequest(this.onValueHelpRequest, this); // Same references

示例 2

在控件实例化上附加事件处理程序,如 ManagedObject\\'s API 参考中所述(所有控件都是 ManagedObjects):

1
2
3
4
new MyInput({
  // ...,
  valueHelpRequest: [/*obj?,*/this.onValueHelpRequest, this]
});

Valid Names and Value Ranges:

  • [...]
  • For events, either a function (event handler) is accepted or an array of length 2 where the first element is a function and the 2nd element is an object to invoke the method on; or an array of length 3, where the first element is an arbitrary payload object, the second one is a function and the 3rd one is an object to invoke the method on [...].

示例 3(针对控件开发人员)

然而,在控件定义中,监听器可以完全省略,因为如果没有传递监听器对象,事件提供者本身(即你的控件实例)默认成为监听器。

1
this.attachValueHelpRequest(this.onValueHelpRequest); // the control instance will be used as the context in that event handler

这在 API 参考中也有描述:

If is not specified, the handler function is called in the context of the event provider.

在 UI5 中使用 Function.prototype.bind 的缺点

  • 在函数上调用 .bind 时,会创建一个全新的函数!

    1
    2
    const myFn = function() {};
    myFn === myFn.bind(); // returns: false

    意味着如果处理程序与 .bind 一起传递,则该处理程序将永远不可分离,因为 detachEvent 等待与调用 attachEvent 时相同的函数引用和相同的侦听器对象引用。

  • 更糟糕的是,使用 .bind 创建的函数将不允许您更改先前传递的 thisArg (this),即使 EventProvider 之后尝试使用不同的 thisArg 来 call 函数。 ECMAScript 规范中描述了此限制(请参阅注 2),以及问题中描述的问题的原因。当ManagedObject克隆template控件进行聚合绑定时,监听器不能被覆盖!