Skip to Content

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.

 

To report this post you need to login first.

5 Comments

You must be Logged on to comment or reply to a post.

  1. Former Member

     

    Hello Reiner,

    really very good Blogs from you!

    We have currently the issue at Text property binding in iterate component charts especially to read out the Dimension text from a hierarchy with the getMember function for each hierarchy node. We only get the text for each article, if the hierachy is disabled, but not for each article hierarchy node we need. Do you have any idea, how we could read out the Hierarchy-Dimension-Text with a hierarchy node?

    Thank you in advance!

    (0) 
  2. Bastian Strehle

    Hello Reiner,

     

    many thanks for sharing this valuable knowledge!

    One question which is quite of some interest for me:
    If you want to retrieve the “Data Selection” of a chart, how would the function look like?

    According to your post I tried this below, also with BindingType.DataSetBinding:

     

    COMPONENTS.getBinding(CH_TEST, “DATA_SELECTION”, BindingType.DataCellBinding);

     

    I am not sure about the property name and cannot find any references for that.

    Alternatives for my approach are warmly welcome 🙂

     

    Could you help me out on that?

     

    Kind regards and have a great day
    Bastian

     

    (0) 
    1. Reiner Hille-Doering
      Post author

      Hello Bastian,

      could you explain what you want to achieve? Normally you would simply modify a data selection using the “setDataSelection” API that all components which support data selection. There is no getDataSelection API, but you can save your selection in a global String variable.

      Using property binding with a data selection property sounds rather exotic to me, because it would take a data cell value from one resultset to update the data selection. If there is really a need for such indirection I would rather write a normal script using getData and setDataSelection.

      Best regards,

       Reiner.

      (0) 

Leave a Reply