Skip to Content

WebDynpro Java has a nice feature called calculated context attributes which I missed a lot in WDA and now in SAP(Open)UI5. Very often we have to perform a data manipulation on UI side to produce “derivatives” – data not directly available from data source but required to the end user. Another example where calculation on the fly is required is the calculation driven by by user input,

Wait, there is concept  of formatter or expression binding in SAPUI5, you might say! Yes, and they work perfectly fine for simple data manipulation within model. By simple I mean – every part of calculation should be explicitly declared and only attributes could be used, like example from here:

https://sapui5.netweaver.ondemand.com/sdk/#docs/guide/07e4b920f5734fd78fdaa236f26236d8.html


oTxt.bindValue({
  parts: [
   {path: "/firstName", type: new sap.ui.model.type.String()},
   {path: "/lastName", type: new sap.ui.model.type.String()},
   {path: "/amount", type: new sap.ui.model.type.Float()},
   {path: "/currency", type: new sap.ui.model.type.String()}
   ],
  formatter: function(firstName, lastName, amount, currency){ // all parameters are strings
   if (firstName && lastName) {
   return "Dear " + firstName + " " + lastName + ". Your current balance is: " + amount + " " + currency;
   } else {
   return null;
   }
   }});




all 4 parameters must be declared even though they all are coming from the same context.

An by within the model means you have to define at least one part for the formatter even that part is not needed.

Those limitations are relatively easy to overcome by dynamic properties declared in JSONModel. What is a dynamic property? It is the property defined by calling Object.defineProperty ( Object.defineProperty() – JavaScript | MDN ).

Why do I call them dynamic? Because you can define getter and setter functions! JavaScript functional nature meets JavaBeans.

Example 1: From version 1.28 there is a control sap.m.MessagePopover available and recommended to use by Fiori Design Guidelines ( https://experience.sap.com/fiori-design/ui-components/message-popover/ ). That control is usually “opened by” some other control, mostly by buttons. So, if there are no active messages in our message manager we would hide that button to prevent users confusion about errors. Another nice addition to that would be – display icon which corresponds to the highest severity in message manager e.g. if there are errors – error icon, if there are just warnings – warning icon and so on. To implement those requirement we would define 2 dynamic properties messagePopoverVisible and messagePopoverButonIcon:


var messagePopoverModelObject = {};
Object.defineProperty(messagePopoverModelObject, "messagePopoverVisible", {
  get : function() {
  var data = sap.ui.getCore().getMessageManager().getMessageModel().getData();
  return data && data.length !== 0;
  }
});
Object.defineProperty(messagePopoverModelObject, "messagePopoverButonIcon", {
  get : function() {
  var data = sap.ui.getCore().getMessageManager().getMessageModel().getData();
  var hasSeverity = function(severity) {
  return function(element, index, array) {
  return (element.type == severity);
  };
  };
  if (data && jQuery.isArray(data)) {
  if (data.some(hasSeverity("Error"))) {
  return "sap-icon://alert";
  }
  if (data.some(hasSeverity("Warning"))) {
  return "sap-icon://alert";
  }
  }
  return "sap-icon://sys-enter";
  }
});
this.getView().setModel(new JSONModel(messagePopoverModelObject), "messagePopoverModel");



now with those properties defined we can use them in the binding:

<Button id=”MessagePopoverButton” press=”handleMessagePopoverPress” visible=”{messagePopover>/messagePopoverVisible}” type=”Emphasized” icon=”{messagePopover>/messagePopoverButonIcon}”/>

Note: MessageManage model could be set for the button control and visibility could be bound via expression binding: “{=$(messageManagerModel>/).length!=0}” but I personally prefer those kind of validation to have in JavaScript – easier to debug and understand.

Example 2: Abap has no boolean type, so if we need to map a true value to ‘X’ and vice verse we can use a dynamic property as it has both, setter and getter (another way to implement – Simple type implementation with formatValue and parseValue).


$.each([ "DisabilityIndicator", "StudentIndicator", "MedicareIndicator" ], function(i, entityName) {
  Object.defineProperty(dataObject, entityName + "Boolean", {
  get : function() {
  return (this[entityName] === "X");
  },
  set : function(value) {
  this[entityName] = (value ? "X" : " ");
  }
  });
});


This example ( from ESS Dependents application) creates dynamically a corresponding value holder which could be used for mapping:

<CheckBox selected="{DisabilityIndicatorBoolean}"/>

Example 3: Very common scenario in business application where we have a header object and items (sales order, PO, shopping cart and so on). Lets say we need to calculate a balance : balance = header amount – sum( item price * item quantity ) The most obvious way would be to implement a method in controller but the problem is – we have to assign a change event handler to every input field involved in calculation (dynamically or even worse – statically). Instead, we would define a dynamic property where we implement a calculation logic in the getter method. The beauty of this approach is that if the static property (amount or qty) from the target model is bound to the UI control, two way binding (default from JSONModel) will cause checkUpdate method execution and as  a result – execution of dynamic property getter (of course only if it is bound to UI control’s property, like Text’s text) AND we have an access to object’s subobject, like items:


Object.defineProperty(itemsModel, "Balance", {
  get: function () {
  var balance = 0.00;
  var itemsAmount = 0.00;
  $.each(this.Items, function (i, item) {
  if (!isNaN(parseFloat(item.Amount)) && !isNaN(parseFloat(item.Qty))) {
  itemsAmount = itemsAmount + parseFloat(item.Amount) * parseFloat(item.Qty);
  }
  });
  balance = this.header.Amount - itemsAmount;
  return balance;
  }
});


and of course as it is a model property we can use it in binding:


<Toolbar id="BalanceBar">
  <content>
  <ToolbarSpacer/>
  <Label text="{i18n>BALANCE}"></Label>
  <ToolbarSpacer width="20px"/>
  <Text text="{ parts : [ 'itemsModel>/Balance' ], formatter: '.formatAmount' }"></Text>
  </content>
</Toolbar>

Limitation – if the dynamic property is declared on the object which should be used in arrays (tables, list etc.) some sort of “cloning” logic should be implemented.

checkUpdate() should be called if data is changed indirectly and affects the calculation logic.

Disclaimer: Those techniques obviously not the only available to implement above mentioned requirements but just give developer more options to consider.

To report this post you need to login first.

Be the first to leave a comment

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

Leave a Reply