How to have control instance as "this" in formatter? - sapui5

I am using SAP UI5 1.52. My formatter file is a separate file and loaded in the controller. But logging this in the formatter returns view instance rather than control instance.
I have referred this question before and tried using absolute path and changed the way object is returned in formatter. It throws an error saying function not found.

UI5 1.69+
View
<MyControl xmlns:core="sap.ui.core" core:require="{ format: 'demo/model/format' }"
property="{
path: '...',
formatter: 'format'
}"
/><!-- Note: remove the dot (.) in front of the formatter function name -->
Formatter
sap.ui.define([], function() { // location: "demo/model/format.js"
"use strict";
return function(data) {
// this === control instance
};
});
As of UI5 1.69, we can require modules directly in the view definition. Requiring and assigning the formatter directly in the binding info lets us to use this as the corresponding control instance.
The documentation mentions it as well:
From the API reference sap/ui/base/ManagedObject#bindProperty:
When the formatter for a property binding (simple or composite) is called, the managed object will be given as this context
From the topic Property Binding:
The this context of a formatter function is generally set to the
control (or managed object) that owns the binding.

In a control's property, you typically write formatter:'.formatter.functionName'
Just change it to: formatter:'namespace.controllerFolder.controllerName.prototype.formatter.functionName'
And this will now refer to the control instance rather than your controller.
Simple and easy :)

Related

Access to methods of UI5 control instance from View [duplicate]

I am using SAP UI5 1.52. My formatter file is a separate file and loaded in the controller. But logging this in the formatter returns view instance rather than control instance.
I have referred this question before and tried using absolute path and changed the way object is returned in formatter. It throws an error saying function not found.
UI5 1.69+
View
<MyControl xmlns:core="sap.ui.core" core:require="{ format: 'demo/model/format' }"
property="{
path: '...',
formatter: 'format'
}"
/><!-- Note: remove the dot (.) in front of the formatter function name -->
Formatter
sap.ui.define([], function() { // location: "demo/model/format.js"
"use strict";
return function(data) {
// this === control instance
};
});
As of UI5 1.69, we can require modules directly in the view definition. Requiring and assigning the formatter directly in the binding info lets us to use this as the corresponding control instance.
The documentation mentions it as well:
From the API reference sap/ui/base/ManagedObject#bindProperty:
When the formatter for a property binding (simple or composite) is called, the managed object will be given as this context
From the topic Property Binding:
The this context of a formatter function is generally set to the
control (or managed object) that owns the binding.
In a control's property, you typically write formatter:'.formatter.functionName'
Just change it to: formatter:'namespace.controllerFolder.controllerName.prototype.formatter.functionName'
And this will now refer to the control instance rather than your controller.
Simple and easy :)

Adding content to XMLView via "addContent" doesn't work

I have the following XMLView:
<mvc:View
xmlns:core="sap.ui.core"
xmlns:mvc="sap.ui.core.mvc"
xmlns="sap.m"
xmlns:data="sap.chart.data"
xmlns:viz="sap.viz.ui5.controls"
xmlns:con="sap.suite.ui.commons"
controllerName="MY_NAMESPACE.controller.ChartView"
xmlns:html="http://www.w3.org/1999/xhtml"
>
<!-- Panel here -->
</mvc:View>
Now, in my controller, I want to dynamically add a sap.m.Panel to the view.
In my onInit function, I pass the object of the current view to the method that creates the Panel and adds it to the view.
onInit: function() {
var sUrl = "/sap/opu/odata/sap/MY_ODATA_SERVICE/",
oModel = new ODataModel(sUrl), // v2
oCurrentView = this.getView();
this.getView().setModel(oModel);
this._createPanel(oCurrentView);
this._createChartContainer();
this._initializeCharts();
this._showCharts();
},
_createPanel: function(currentView) {
var sId = this._globals.panelId;
var oViewPanel = new Panel(sId, {
width: "auto"
}).addStyleClass("sapUiSmallMarginBeginEnd");
this._globals.panelState = oViewPanel;
currentView.addContent(oViewPanel);
return currentView;
},
However, the Panel is never rendered:
But when I call the getContent function of the view, the panel is listed as an entry.
Clarification:
Creating a sap.m.Panel in the XMLView isn't a problem. Placing this bit of XML into the XMLView works.
<Panel id="chartPanel"
class="sapUiSmallMarginBeginEnd"
width="auto"
></Panel>
But, I need to create and append the sap.m.Panel object to the XMLView at runtime (in the controller), not in the XMLView.
Now, the problem:
With above posted controller code, the panel objects gets created. In fact, it even gets registered as a content aggregation of the XMLView, but it simply doesn't get rendered (see picture above).
Any suggestion on why and how this behaviour occurs are greatly appreciated.
Issue
this.getView().addContent(/*...*/) doesn't work.
Why
Currently, XMLView won't allow manipulating its content via APIs as the documentation warns:
Be aware that modifications of the content aggregation of this control are not supported due to technical reasons. This includes calls to all content modifying methods like addContent etc., but also the implicit removal of controls contained by the content aggregation. For example the destruction of a Control via the destroy method. All functions can be called but may not work properly or lead to unexpected side effects.
This is, at the time of writing (v1.64), still the case.
PS: The above limit applies only to XMLView. Other view types, such as JSView*, are not affected.
* sap.ui.core.mvc.JSView and sap.ui.jsview are deprecated. Use Typed Views instead (Applicable since v1.90).
try to put the Panel inside the XML view and give it a property visible="false".
<Panel id="panelId" visible="false">
</Panel>
In your function you could do something like this:
_createPanel: function(){
var oPanel = this.getView().byId("panelId");
oPanel.setVisible(true);
// Other Methods for Panel
}
With the oPanel instance you can execute all methods listed in the API:
https://sapui5.hana.ondemand.com/#/api/sap.m.Panel
Hope this helps :-)
Best regards

How can I access the AST that VSCode creates

I write a VSCode Extension for UI5 JavaScript. The most missing Feature is to have IntelliSense for UI5. Using UI5 typings it will work but not in all.
This works:
var testvar1 = new sap.m.Button();
Now i can use IntelliSense in VSCode in the testvar1.
The Problem e.g.:
sap.ui.define([
"sap/ui/core/mvc/Controller"
],
function (Controller) {
"use strict";
return Controller.extend("", {
});
});
In this case there is a Controller variable in the function, this variable is defined with the Namespace before. I search now for a possibility to assign this variable in my Extension with the Namespace. I know this can be done in the AST but i have now idear how to get access to the AST to set:
Controller = sap.ui.core.mvc.Controller
The goal is to have the full IntelliSense now in the variable Controller
I hope it is clear what i want, so far.
If you only need to give a type to Controller, try using JSDoc:
sap.ui.define([
"sap/ui/core/mvc/Controller"
], function (/** #type {sap.m.Controller} */Controller) {
// ...
});
To answer the original question, you can access/modify the AST with a TypeScript server plugin. This is not trivial so I would try to avoid doing this unless you really need to.

Complex Binding in SAPUI5 XML View

I am trying to convert a string value to Boolean while binding it from a JSONModel. Ideally the value in my model is "true"/"false" and I want to bind it to the visible property of an item. The model is defined to be TwoWay binding but I guess that does not matter in this case
I have declared "complex binding" in the index.html.
data-sap-ui-xx-bindingSyntax="complex"
Then I create my XML view and bind the property from the model as below:
<P13nColumnsItem>
columnKey="{tableVariantAFModel>Fieldname}"
visible="{path:'tableVariantAFModel>Visible', type: 'sap.ui.model.type.Boolean', mode: 'sap.ui.model.BindingMode.TwoWay'}"
index="{tableVariantAFModel>DisplayOrder}">
<P13nColumnsItem>
When I run my app,it throws the below error:
Is there any step I am missing?
Also, I need to add this app to the Fiori Launchpad, so I need to define the complex binding in manifest.json file rather than in index.html . Where can I define it in the manifest file.
For simple use cases like this you can use an expression binding instead of implementing additional logic somewhere.
<P13nColumnsItem>
columnKey="{tableVariantAFModel>Fieldname}"
visible="{= ${tableVariantAFModel>Visible} === 'true'}"
index="{tableVariantAFModel>DisplayOrder}">
<P13nColumnsItem>
i would advise to use a formatter. See here. In the formatter you could write:
visible="{path:'tableVariantAFModel>Visible', formatter: '.formatter.stringToBoolean'}"
in the formatter you could create the function like:
stringToBoolean: function(_stringBoolean){
(_stringBoolean === "true") ? return true : return false;
}
You have to make sure that you instantiate the formatter in your controller, or optionally you could choose a function in your controller itself.

Setting row class for sap.m.Table rows

I'm trying to assign a class to a row in my sap.m.Table dynamically using my view model SearchResults.
Unfortunately it's ignoring my class properties (see below).
How do I do this in my XML view ?
<ColumnListItem class="{SearchResults>typeClass}">
To apply CSS properties to row, you will also need take predefined CSS class of table .sapMListTbl along with your custom CSS class like I have done here.
I would suggest you to use formatter to apply these classes along with class from your model.
Unfortunately the class attribute is not bindable since it is not a property.
As a workaround you could do sth. like this:
<ColumnListItem visible="{
path: 'SearchResults>typeClass',
formatter: 'my.formatter.formatClass'
}" />
my.formatter.formatClass = function(typeClass) {
// in a static formatter this refers to the control, here your ColumnListItem
this.addStyleClass(typeClass);
// just always return true for the visible property
return true;
}
Note: The formatter can't be a member of your controller since then you won't be able to refer to the control (this will be the controller itself). Hence the formatter needs to be a static function.
Solution found!!!!
http://scn.sap.com/community/developer-center/front-end/blog/2016/05/17/coloring-table-rows-conditionally-in-sap-ui5-xml-views-based-on-odata-service
Hopefully this help anyone who's had the same problem...