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.
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.
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.onVHRequest, this); // No .bind!
// ...
myInput.detachValueHelpRequest(this.onVHRequest, this); // Same references
Attaching an event handler on control instantiation as documented in ManagedObject
's API reference (All controls are ManagedObjects):
new MyInput({
// attach
valueHelpRequest: [/*obj?,*/this.onVHRequest, this]
});
// ...
myInput.detachValueHelpRequest(this.onVHRequest, 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 [...].
If this
in the event handler is supposed to be the object that provides the event, passing the listener object can be omitted when registering the 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.
Function.prototype.bind
in UI5When 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.
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!