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
In my previous posts about dynamic apps in Lumira 2.1 I have missed another cool feature. In the first part about the Components API I have skipped the functions createBinding and getBinding. They allow you to bind properties a almost any component to data from the resultset - and also change the binding later.

But let's first recapitulate what Property Binding is about.

Property Binding


Apps consist of components and components have properties. A property can be set to a static value using Designer's Properties View. You can also modify property values using the typical "set<PropertyName" APIs. E.g. a Text component has a "TEXT" property which you can read with getText and change with setText APIs.

Since Design Studio 1.5 you have an attractive alternative. In the Properties View you find a the "Binding" column with "+" signs for all bindable properties:



 

After clicking the "+" sign, you can choose a binding type (if there is only one, it is preselected and can't be changed) and fill the properties of the binding:



In the sample I have bound the "TEXT" property to the value of a data cell from DS_1. Whenever the cell value changes, the property is automatically updated. I could have achieved the same using the following script on DS_1's onResultsetChanged event.
var s = DS_1.getDataAsString("", {"(MEASURES_DIMENSION)":"store_sales"});
TEXT_1.setText(s);

But Property Binding is easier to configure and saves you from having long and complex onResultsetChanged scripts. The disadvantage however is that Property Binding is fairly static. If DS_1 points to a different data source with different measure and dimension names, the Property Binding configuration will most likely not fit anymore.

The new APIs add exactly the missing dynamics. But before I explain the new APIs you should understand how Property Binding works behind the scenes.

How it Works


Property Binding are in fact small invisible data-bound components. Like a Crosstab is informed and updated whenever its result set changes, those binding components are informed, retrieve the value and set the target property accordingly. The configuration of a Property Binding consists simply of the property values of the binding component. But unlike other components, the binding components don't have a name. Therefore you can't do something the following script to change the binding to the store cost measure:
PROPERTY_BINDING_1.setDataSelection({"(MEASURES_DIMENSION)":"store_cost"});

Changing a Property Binding


Instead you use the new API COMPONENTS.getBinding to retrieve a binding component. Afterwards you can easily change its properties:
var binding = COMPONENTS.getBinding(TEXT_1, "TEXT", BindingType.DataCellBinding);
if (binding !== undefined) {
binding.setDataSelection({"(MEASURES_DIMENSION)":"store_cost"});
}

COMPONENTS.getBinding has three arguments:

  1. The component with the bound property

  2. The technical name of the property. We don't officially document them - and the Property View only shows the human-friendly title. But you could take a look into context.biapp or content.bicomp to find the right one.

  3. The binding type. Like the component type enumeration used in COMPONENT.createComponent, the list is populated automatically based on the available Property Binding types. You need to specify it, as one property might be bound with multiple Property Bindings.


The function is generic and returns the correct binding component - or undefined, if the property is not bound with the given binding type.

In the example the returned DataCellBinding component has two property setter APIs, setDataSelection and setDataSource. For third property "Formatter Function" can not be set by scripting. JavaScript allows passing functions as argument to another functions, but such a higher-level functional programming concept is by far to exotic for a business scripting language 🙂 .

Creating a Property Binding


Binding a property is almost the same as modifying an existing binding:
var binding = COMPONENTS.createBinding(TEXT_1, "TEXT", BindingType.DataCellBinding);
binding.setDataSource(DS_1);
binding.setDataSelection({"(MEASURES_DIMENSION)":"store_cost"});

If the property could be bound using the given binding type, the returned object is a newly created binding component - which can be initialized as needed. Note the not all property can be found with each binding type. If the given property can't be bound with the given binding type, the function returns undefined.

You can also bind properties of component that you have created dynamically. In my blog Dynamic Apps in Lumira 2.1 – Iterating Over a Resultset I have shown a dynamically created table from Text components. It has one disadvantage: Whenever the data changes, I have to remove all Text components and recreate them to reflect the actual data. If only the numbers change a bit, it is much nicer to bind the properties to the data. Here is the modified version:
var s = DS_1.getDataSelections({
"customer_id": "?"
});

s.forEach(function(sel, index) {
var pos = index * 25 + 40;
var m = DS_1.getMember("customer_id", sel);
var tText = COMPONENTS.createComponent(ComponentType.Text);
tText.setText(m.text);
tText.setWidth(150);
tText.setTopMargin(pos);
tText.setLeftMargin(10);

var tBirthdate = COMPONENTS.createComponent(ComponentType.Text);
var birthdayString = m.getAttributeMember("birthdate").text;
tBirthdate.setText(birthdayString.substring(0, birthdayString.length - 9));
tBirthdate.setTopMargin(pos);
tBirthdate.setLeftMargin(150);
tBirthdate.setWidth(200);


var tSales = COMPONENTS.createComponent(ComponentType.Text);
var binding = COMPONENTS.createBinding(tSales, "TEXT", BindingType.DataCellBinding);
binding.setDataSource(DS_1);
binding.setDataSelection({"(MEASURES_DIMENSION)":"store_sales","customer_id": m.internalKey});
tSales.setTopMargin(pos);
tSales.setLeftMargin(400);
tSales.setWidth(50);
tSales.setCSSClass("bold right");
});

Unbinding a Property


As Property Binding is based on simple technical components, removing a Property Binding is as simple as deleting a component:
var b = COMPONENTS.getBinding(TEXT_1, "TEXT", BindingType.DataCellBinding);
if (b !== undefined) {
COMPONENTS.deleteComponent(b);
}

Afterwards the property has its last value until it is bound again or updated, e.g. with TEXT_1.setText().

Multiple Data Sources for SDK Components


So far I have only used BindingType.DataCellBinding. The other interesting binding type is BindingType.DataSetBinding. It can be used on Data Selection like properties of Chart, Spreadsheet and several SDK components. The DataSetBinding combines the two properties "Data Source" and "Data Selection" into one. This is especially useful if an SDK component has multiple properties of this kind. E.g. the "Simple Table" SDK sample has 3 properties of this type, called "column1" "column2" and "column3". Normally you would set the Data Source property e.g. to "DS_1" and assign different data selections to the 3 columns. But you could also keep the Data Source property blank and bind the columns to different data sources using a DataSetBinding.

This was already possible since Design Studio 1.5, but now you can also do this with scripting:
var simpleTable = COMPONENTS.createComponent(ComponentType.com_sap_sample_simpletable_SimpleTable);

var col1 = COMPONENTS.createBinding(simpleTable, "column1", BindingType.DataSetBinding);
col1.setDataSource(DS_1);
col1.setDataSelection({"(MEASURES_DIMENSION)":"unit_sales"});

var col2 = COMPONENTS.createBinding(simpleTable, "column2", BindingType.DataSetBinding);
col2.setDataSource(DS_2);
col2.setDataSelection({"(MEASURES_DIMENSION)":"Old_Dependency_Ratio_2006"});

var col3 = COMPONENTS.createBinding(simpleTable, "column3", BindingType.DataSetBinding);
col3.setDataSource(DS_3);
col3.setDataSelection({"(MEASURES_DIMENSION)":"COST"});

Note that the Simple Table SDK sample needs some changes for this sample to work.

Outlook


The two binding types DataCellBinding and DataSetBinding are the only ones that are fully supported in Lumira 2.1 - but they are anyway the most commonly used ones. We might extend it a bit in the future, especially if you have ideas for use cases that can extend the range for Lumira - and for apps and dashboards built with it.

 
8 Comments