Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
322 views
in Technique[技术] by (71.8m points)

javascript - How to properly attach and detach event handler in UI5

I have problems with data binding of my custom control.

My control inherits from sap.m.Input and extends it with a special value helper. One of my new properties of my new control is a simple header for the value help dialog. This is bound to an i18n model.

When I now use my control in a normal form, everything works. The title is bound correctly and shows the value of the bound i18n property in that model. If I use my control as a template in a column of a sap.ui.table control, it only shows the default value of the title property. Data binding does not seem to work. But is still working on the inherited properties (such as value).

For simplification here my control which now has only that title property and if value help is requested, it shows the current value in an alert box. In table, it shows the default value. And without table, it shows the bound value from i18n model.

Here the simplified control code:

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);
      },

    });
  });
});

Usage in sap.ui.table.Table (which doesn't work and shows the default value of the title property):

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

Usage which works:

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

Once again, binding against the value property works in both ways. Problem only exists with my own property vhTitle. Any Ideas are welcome.

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

Do NOT use .bind when attaching event handlers to ManagedObject's events. The same applies to detaching event handlers. UI5 has its own documented mechanism for passing listener objects for those cases.

Example 1

Attaching / detaching a valueHelpRequest-handler using the corresponding APIs and passing values to the list of arguments as documented in the API reference:

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

Example 2

Attaching an event handler on control instantiation as documented in ManagedObject's API reference (All controls are 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 [...].

Example 3 (For control developers)

In control definition, however, the listener can be omitted completely because the event provider itself (i.e. your control instance) becomes the listener by default if no listener object is passed.

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

This is described in the API reference as well:

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

Drawbacks of using Function.prototype.bind in UI5

  1. When calling .bind on a function, an entire new function is created!

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

    Meaning if a handler is passed with .bind, that handler becomes never detachable because detachEvent awaits the same function reference and the same listener object reference as when attachEvent was called.

  2. To make things worse, the function created with .bind won't let you change the previously passed thisArg (this) even if the EventProvider tries to call the function afterwards with a different thisArg. This limitation is described in the ECMAScript specification (See Note 2), and also the cause of the issue described in the question. When ManagedObject clones the template control for aggregation binding, the listener cannot be overwritten!


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...