Technology Blogs by SAP
Learn how to extend and personalize SAP applications. Follow the SAP technology blog for insights into SAP BTP, ABAP, SAP Analytics Cloud, SAP HANA, and more.
cancel
Showing results for 
Search instead for 
Did you mean: 
reiner_hille-doering
Active Contributor
Maybe you like SAPUI5 like I do. Then for sure you know the great SAPUI5 sample and documentation pages SAPUI5 Explored or SAPUI5 Demo kit. There you can easily browse though all available SAPUI5 controls and documentation about all properties and functions. But the greatest are the samples: Just run them - and if you like one, you find all source code in form of some XML and JavaScript snippets.



Wouldn't it be great to use such content directly in your Design Studio or Lumira Desginer app? - Without writing a SDK component and dealing with JSON data formats etc? Here you go.

 

What You Need


You need two ingredients:

  • To access analytical data in SAPUI5, you need an SAPUI5 model implementation. Fortunately I have implemented it already as part of Design Studio 1.6, it ships in all later versions including Lumira 2.x. It has been undocumented so far, but I think in the meantime it is mature enough to be used when needed.

  • A little SDK component that allows to use so-called SAPUI5 XML Views directly in your app without a need for additional components. I'm currently integrating this component into SCN Community package for Lumira 2.x and for Design Studio. In the meantime you find the source code here.


 

Try It Without Data


If you have the XMLView component installed, you can try it without coding. Create one instance in Designer and ensure that the Additional Properties View is visible.

In the SAPUI5 Demo kit, find the sample, then switch to source view.



 

Now copy the XML view content to the upper part and the Controller JavaScript code to the lower part of the Additional Properties View.



Cool, isn't it? You can immediately run the app, modify the XML or JavaScript or even add multiple XMLViews and other components.

 

Use Data


The sample above is quite nice, but also quite useless: The values that the charts display are hard-coded sample values.

I promise you that you will be able to use any SAPUI5 with any Lumira data source when you have read the whole article, but at the beginning let's use a very simply sample that should work with most data sources without modifications.

Add a data source to your app and drag it over the XMLView component to bind it (or set the "Data Source" property in the Properties View).

Now add the following XML to the upper part of the Additional Properties View. The lower part should stay empty.
<mvc:View
xmlns:l="sap.ui.layout"
xmlns:core="sap.ui.core"
xmlns:mvc="sap.ui.core.mvc"
xmlns="sap.m">
<List
id="dimensions"
items="{/dimensions/(MEASURES_DIMENSION)/members}">
<StandardListItem
title="{text}"
description="{key}"/>
</List>
</mvc:View>

 

You will see a list of measures in a list box.



 

How Does It Work


Lumira uses SAPUI5 internally. If you use SAPUI5 directly, you will recognize that, both, Lumira and SAPUI5 have much in common. Both allow you to assemble UI elements. Both  follow the Model-View-Controller design principles - and both contain a powerful Data Binding feature. The following table lists some aspects:







































Aspect Lumira Designer SAPUI5
Elements Component Control
Data is coming from Data Source (BW, HANA, ...) Model (JSON Model, ODataModel)
Logic is written in Event Handlers Controller
UI is stored in App or Composite View (e.g. XML View
Bind data to a complex element Set data source property List Binding
Bind data to property Use a Property Binding Binding Path


 

One important difference between Lumira Event Handlers and SAPUI5 Controllers: Event Handlers are executed on the server (in Rhino script engine), while Controller JavaScript code runs on the browser.

You can connect the two using properties and events and the SDK functions firePropertiesChanged, fireEvent, callZTLFunction etc. As the set of properties and events of XMLView can't be changed dynamically, I have prepared and "onSelect" event and the properties "selection" and "selectionType". XMLView provides several script API, e.g. getSelection, getDataSelection, and so forth. Take a look a the source code: it contains several tricks that you might find interesting.

In the samples below I have some cases where the controller sends data - and BIAL script uses it.

 

The other area where Lumira and SAPUI5 are different and incompatible is the concept of Data Binding. To close the gap, I have implemented the "Lumira SDKModel for SAP UI5" ("SDKModel" for short), which solves this gap. It provides a Model for SAPUI5 data binding - based on Lumira data sources.


The SDKModel provides all data from a SDK Result Set JSON via the SAPUI data binding Model API, just as the existing models JSONModel and ODataModel do.

 

Binding Syntax


Once the model is attached, almost all properties of an SAPUI5 control can be bound. In XML views, a property is bound using the "{<bindingPath>}" syntax. A Binding Path consists of one or more parts, starting with and separated by slash signs, for example "{/<part1>/<part2>/<part3>}". This concept is the same for all models, but the syntax and meaning of the parts depends on the model type. E.g. SDKModel has different supported parts than ODataModel or JSONModel.

Simple and List Binding


Depending on the type of the bound property, the model must either provide a single value or a list. In case of single values, SAPUI5 also supports type conversion and formatting. For details see the SAPUI5 documentation.

Properties that bind to a list are typically used for aggregations (which would normally contain child controls). For example, a Listbox control has an "items" property that is normally filled with a number of ListboxItem controls. But for data binding, you bind the "items" property to a list or array of the model and provide a single templateListboxItem control. For each entry in the bound list, SAPUI5 will automatically clone the template and connect it with the entry from the model. Therefore, the template can use a relative binding path (without a leading slash) which will be used to bind relatively to a list entry.

In the example above, listbox's "items" propertiy might be bound to "{/something}" and the ListboxItem template has a "text" property bound to "{else/value}". SAPUI5 will create ListboxItems for each entry. And each ListboxItem's "text" property is bound to "/something/0/else/value", "/something/1/else/value", and so forth.

In effect, the binding path is a concatenation of the path of the list-bound property, the index, and the relative path of the template. This approach can be arbitrarily nested, having controls containing controls containing controls, etc., always propagating bound parts of the model to the children.

Master Data Paths


The following paths access master data (sometimes also called meta data), that is, data that is on an axis, such as dimensions, members, attributes, etc.

The model directly accesses the SDK JSON and thus corresponds to the JSONModel documentation. In all arrays you can either specify the index, the key, or a placeholder (like "(MEASURE_DIMENSIONS)" ).

 































































Path

Description
/dimensions List of dimensions; column dimensions first, then row dimensions
/dimensions/0 First dimension - which is the dimension on columns, index 0.
/dimensions/<key> Dimension with the given internal key
/dimensions/(MEASURES_DIMENSION) The dimension containing the measures. You can use the placeholder "(MEASURES_DIMENSION)" also for all subsequent path instead of index or key.
/dimensions/<indexOrKey>/key Key of the specified dimension
/dimensions/<indexOrKey>/text Language-dependent name of the dimension
/dimensions/<indexOrKey>/axis "COLUMNS" | "ROWS": Axis on which the dimension is located
/dimensions/<indexOrKey>/axis_index  Axis tuple index of the dimension, >= 0
/dimensions/<indexOrKey>/containsMeasures true|false: Does dimension contain measures?
/dimensions/<indexOrKey>/attributes List of attributes (not valid when result set has no attributes)
/dimensions/<indexOrKey>/attributes/0/key The key of the first attribute of the given dimension
/dimensions/<indexOrKey>/attributes/<indexOrKey>/text The text of the given attribute if the given dimension
/dimensions/<indexOrKey>/members Array of dimension's members


 

Samples


The following sample shows all dimensions from the result set in a list box and uses the dimension's text as item title and the dimension's key as description.

List of Dimensions




XML View:



<mvc:View
controllerName="sap.m.sample.ListSelection.List"
xmlns:l="sap.ui.layout"
xmlns:core="sap.ui.core"
xmlns:mvc="sap.ui.core.mvc"
xmlns="sap.m">
<List
id="dimensions"
selectionChange="handleSelectionChange"
mode="SingleSelect"
items="{/dimensions}">
<StandardListItem
title="{text}"
description="{key}"/>
</List>
</mvc:View>





Controller:


sap.ui.define([
'sap/ui/core/mvc/Controller'
], function(Controller) {
"use strict";

var ListController = Controller.extend("sap.m.sample.ListSelection.List", {

onInit : function (evt) {
},

handleSelectionChange: function (oEvent) {
debugger;
var selectedKey = oEvent.getParameter("listItem").getDescription();
var comp = this.getView().getParent();
var sel = {};
sel[selectedKey] = ["*"];
comp.setSelection(sel);
comp.setSelectionType("DIMENSION");
comp.fireDesignStudioPropertiesChangedAndEvent(["selection", "selectionType"], "onSelect");
}
});

return ListController;
});


 

BIAL Script on XMLVIEW_1's onClick event:
if (me.getSelectionType() == ResultSetSelectionType.DIMENSION) {
var selection = me.getSelection();
var dimName = "";
selection.forEach(function(value, key) {
dimName = key;
});

APPLICATION.alert(dimName);
}

 


List of Measures



The following sample shows a list of measures and uses the measure's text as item title and the measure's key as description.


XML View:



<mvc:View
controllerName="sap.m.sample.ListSelection.List"
xmlns:l="sap.ui.layout"
xmlns:core="sap.ui.core"
xmlns:mvc="sap.ui.core.mvc"
xmlns="sap.m">
<List
id="dimensions"
selectionChange="handleSelectionChange"
mode="SingleSelect"
items="{/dimensions/(MEASURES_DIMENSION)/members}">
<StandardListItem
title="{text}"
description="{key}"/>
</List>
</mvc:View>


 

Controller:
sap.ui.define([
'sap/ui/core/mvc/Controller'
], function(Controller) {
"use strict";

var ListController = Controller.extend("sap.m.sample.ListSelection.List", {
onInit : function (evt) {
},

handleSelectionChange: function (oEvent) {
debugger;
var selectedKey = oEvent.getParameter("listItem").getDescription();
var comp = this.getView().getParent();
comp.setSelection({"[Measures]": [selectedKey]});
comp.setSelectionType("MEMBER");
comp.fireDesignStudioPropertiesChangedAndEvent(["selection", "selectionType"], "onSelect");
}
});
return ListController;
});

 

BIAL Script on XMLVIEW_1's onClick event:
if (me.getSelectionType() == ResultSetSelectionType.MEMBER) {
var member = me.getSelectedMember("[Measures]");
APPLICATION.alert(member.text);
}

Data Paths


While the paths for master data are - except from the support to use keys - similar to the JSONModel, the access to the data area follows a different approach: A set of data cells is selected using a data selection JSON. All such data selections on the path are combined until you eventually select a single data cell. From the cell you can get value, formattedValue and more.

The data selection JSON syntax is known from many places, e.g. getData APIs and chart data selection. It has the form "'<dim1>': '<member1>', 'dim2': ['<member3>, '<member4>'" etc. Note that here we used single quotes inside the JSON, which is handy if the expression is placed in a string surrounded by double quotes. Also note that the braces ({ }) are omitted in selection JSONs in binding paths. Don't confuse the braces used for SAPUI5 binding in general with the braces surrounding data selection JSONs.

If you use '*" as pseudo key for a member, SDKModel will create a list binding - producing a list with one entry for each member of the dimension. You can use the '*' also for multiple dimensions to iterate over the combinations. The same happens with "?", only that the totals are skipped. You know this concept from the new getDataSelections API to iterate over result sets.

 















































Path

Description
/'dim1': '*' Keep all cellls - but iterate over the different members of dim1
/'dim1': 'm11', 'dim2': '*' Reduce data to all cells with dimension1 being 'm11' and iterates over all members of dim2
/'dim1': '?' Same as "/'dim1': '*'", but skip totals
/'dim1': '*'/0 First member of the list created by "/'dim1': '*'". The result is the same as "'dim1': 'm11'" (assuming that 'm11' is the first member of dim1 in the result set. Depending in the navigation state this expression might select one or more cells.
/'dim1': '*'/0/value The float value of the first data cell selected by "'dim1': 'm11'". If "'dim1': 'm11'" selected multiple cells, the value might not be the expected one. In this case it would be better to refine the selection.
/'dim1': '*'/0/formattedValue The formatted string value of the first data cell selected by "'dim1': 'm11'". If "'dim1': 'm11'" selected multiple cells, the value might not be the expected one. In this case it would be better to refine the selection.
/'dim1': '*'/0/'dim2': 'm21'/value The float value of the first cell selected with "'dim1': 'm11, 'dim2': 'm21'".
/'dim1': '*'/0/'dim2': '*' Same as "/'dim1': 'm11', 'dim2': '*'": Reduce the data to all cells with dimension1 being 'm11' and iterates over all members of dim2
/'dim1': '*'/0/format/LocalException-1 If "LocalException-1" is the key of a conditional format, and the cell has some level for this conditional format type assigned, you will receive a number between 0 and 9. Else it will be null.


 

Samples


Let's assume for the next samples that you have a dimension with key "store_city" in the initial view and a measure with key "store_cost".

 

List with data





<mvc:View
xmlns:l="sap.ui.layout"
xmlns:core="sap.ui.core"
xmlns:mvc="sap.ui.core.mvc"
xmlns="sap.m">
<List
id="data"
items="{/'store_city': '*', '(MEASURES_DIMENSION)': 'store_cost'}">
<StandardListItem
title="{value}"
description="{formatedValue}"/>
</List>
</mvc:View>





Note: If you have more dimensions in the initial view, the numbers shown in the list might not be the expected ones. In this case also specify the other dimensions, for example, using placeholder "(RESULT_MEMBER)".

Master Data for Data Cells


If you have a path pointing to a data cell selection (for example, from a parent list binding), you can navigate to the corresponding member master data using a <dimension_name> path.















Path

Description
/'dim1': '*'/0/dim1/text Returns the member text of the first member of dim1. Identical to "/dimensions/dim1/members/0/text" (assuming that member0 also appears in the result set as first member).


Samples


The following sample shows a Grid Table with two columns. Rows iterate over dimensions "store_city". The first column shows the corresponding member text. The second column shows a data cell value.

 

Simple Table





<mvc:View
xmlns="sap.ui.table"
xmlns:mvc="sap.ui.core.mvc"
xmlns:u="sap.ui.unified"
xmlns:c="sap.ui.core"
xmlns:m="sap.m">
<Table
rows="{/store_city:'*'}"
title="Cities"
visibleRowCount="7">
<columns>
<Column width="12rem">
<m:Label text="Store City"/>
<template>
<m:Text text="{store_city/text}"/>
</template>
</Column>
<Column width="11rem">
<m:Label text="Store Cost"/>
<template>
<m:Text text="{'(MEASURES_DIMENSION)': 'store_cost'/formattedValue}"/>
</template>
</Column>
</columns>
</Table>
</mvc:View>


 

 

Cascading Data Selections


You can make data selections smaller and smaller - nesting list components into other list components. For example, a table could have multiple lines, each line bound to a data selection that contains many cells. Some columns can select total values from such a block. Some other columns can contain complex controls, such as lists or micro charts that show multiple values of the remaining dimensions.

Samples


The following sample shows a table with three columns. We assume that the initial view contains dimensions "product_id", "gender", and measures on the axes.  The rows iterate over the dimension "product_id" - showing one line for each product. The fist column shows the product - with key and text. The second column shows the total sales - independent of dimension "gender". As the selection here contains multiple cells with unspecified dimension "gender",  we need to use a "(RESULT_MEMBER)" refinement selection. The third column shows details on dimension "gender" in a nested dropdown box. It iterates over the members of dimension "gender" and shows the details.

 

Table with Dropdown Boxes





<mvc:View
controllerName="sap.m.sample.ListSelection.List"
xmlns="sap.ui.table"
xmlns:mvc="sap.ui.core.mvc"
xmlns:u="sap.ui.unified"
xmlns:c="sap.ui.core"
xmlns:m="sap.m">
<Table
id="theTable"
rows="{/product_id:'*'}"
title="Products"
selectionMode="MultiToggle"
rowSelectionChange="onTableRowSelected"
visibleRowCount="7">
<columns>
<Column width="20rem">
<m:Label text="{/dimensions/product_id/text}"/>
<template>
<m:Text text="{product_id/text} ({product_id/key})"/>
</template>
</Column>
<Column width="8rem">
<m:Label text="{/dimensions/(MEASURES_DIMENSION)/members/store_sales/text}"/>
<template>
<m:Text text="{'(MEASURES_DIMENSION)': 'store_sales', gender: '(RESULT_MEMBER)'/value}"/>
</template>
</Column>
<Column width="10rem">
<m:Label text="{/dimensions/(MEASURES_DIMENSION)/members/store_sales/text} Details"/>
<template>
<m:Select change="onDropdownSelect"
width="100%" items="{'(MEASURES_DIMENSION)': 'store_sales', 'gender': '*'}">
<c:Item key="{gender/key}" text="{gender/text}: {value}"/>
</m:Select>
</template>
</Column>
</columns>
</Table>
</mvc:View>


Controller:
sap.ui.define([ 'sap/ui/core/mvc/Controller' ], function(Controller) {
"use strict";
var ListController = Controller.extend("sap.m.sample.ListSelection.List", {

onInit : function(evt) {
},

onTableRowSelected : function(oEvent) {
debugger;
this.sendSelection("DATA");
},
onDropdownSelect : function(oEvent) {
debugger;
var dropdown = oEvent.getSource();
var key = dropdown.getSelectedKey();
this.sendSelection("DROPDOWN", key);
},
sendSelection : function(selectionType, gender) {
var table = this.byId("theTable");
var indexes = table.getSelectedIndices();
var selectedProductKeys = [];
for (var i = 0; i < indexes.length; i++) {
var index = indexes[i];
var context = table.getRows()[index].getBindingContext();
var key = context.getProperty("product_id/key");
selectedProductKeys.push(key);
}
var comp = this.getView().getParent();
var selection = {
"product_id" : selectedProductKeys
};
comp.setSelection(selection);
if (gender) {
selection.gender = gender;
}
comp.setSelectionType(selectionType);
comp.fireDesignStudioPropertiesChangedAndEvent([ "selection", "selectionType" ], "onSelect");
}

});

return ListController;

});

BIAL Script on XMLVIEW_1's onClick event:
var selection = me.getSelection();
CHART_1.setDataSelection(selection);
if (me.getSelectionType() == "DROPDOWN") {
APPLICATION.alert("Dropdown: " + selection["gender"][0]);
}

 

The result is a nice table with drop down controls in the cells:



 

Conclusion


I must admit that writing the correct binding paths is not simple. But it should still take less time than rewriting everything from scratch.

As promised, you should be able to bind a microchart from the SAPUI5 sample. The XML would be something like
<mvc:View
xmlns="sap.suite.ui.microchart"
xmlns:m="sap.m"
xmlns:mvc="sap.ui.core.mvc">
<ComparisonMicroChart scale="M" class="sapUiSmallMargin" press="press" width="30rem" minValue="-30" maxValue="70"
colorPalette="#5cbae6, #b6d957, #fac364, #8cd3ff, #d998cb, #f2d249, #93b9c6, #ccc5a8, #52bacc, #dbdb46, #98aafb"
data="{/'store_city': '?', '(MEASURES_DIMENSION)': 'store_sales'}">
<data>
<ComparisonMicroChartData title="{store_city/text}" value="{value}"/>
</data>
</ComparisonMicroChart>
</mvc:View>

 

and shows up in Designer like this:



 

If you need a table representation, you should spend some time looking into the Scorecard component. Internally it uses the XML View and SDKModel, but provides some help to configure all the binding logic in its Additional Properties View. This view also helps understanding the binding expression syntax.

The SDKModel can be also useful for more specific SDK components. Let's assume that you have an SDK component with handler type "sapui5" and a data-bound property "data". Then you can require an SDKModel, fill it with the result set JSON and hand it over to SAPUI5's setModel API.
 define(["sap/designstudio/sdk/SDKModel"], function(SDKModel) {
<soneSuperType>.extend("<Id>", {
setData: function(json) {
var model = new SDKModel(json);
this.setModel(model);
},
// ...
});
});

For more details you might also have a deeper look into the XMLView sample component.
18 Comments