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

How to properly attach and detach event handler in UI5

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

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

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

为了简化,我的控件现在只有那个标题 属性,如果请求值帮助,它会在警告框中显示当前值。在table中显示默认值。没有 table,它显示来自 i18n 模型的绑定值。

这里是简化的控制代码:

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 中的用法(不起作用并显示标题的默认值 属性):

<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>         

有效的用法:

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

再一次,对值 属性 的绑定以两种方式起作用。问题只存在于我自己的 属性 vhTitle。欢迎任何想法。

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

示例 1

Attaching / detaching 一个 valueHelpRequest-处理程序使用相应的 APIs 并将值传递给参数列表 ,如 在 API参考:

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

示例 2

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

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(针对控件开发人员)

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

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

这在 API reference 中也有描述:

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

在 UI5

中使用 Function.prototype.bind 的缺点
  1. 在函数上调用 .bind 时,会创建一个完整的 new 函数!

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

    意味着如果处理程序通过 .bind 传递,那么 处理程序将永远无法分离 因为 detachEvent 等待相同的函数引用和相同的侦听器对象引用就像调用 attachEvent 时一样。

  2. 更糟糕的是,使用 .bind 创建的函数不会让您更改先前传递的 thisArg (this ) 即使 EventProvider tries to call the function afterwards with a different thisArg. This limitation is described in the ECMAScript specification (见注释 2),也会在问题中描述问题的原因。 ManagedObject克隆聚合绑定template控件时,无法覆盖监听!