Skip to Content
Technical Articles

Integrating Custom SAPUI5 Components with SAP Screen Personas

Table of Contents

Motivation

Many SAP Screen Personas customers are using the product’s powerful JavaScript scripting engine to extend SAP GUI applications via the SAPUI5 framework.  These customers frequently leverage GuiHtmlViewer web application containers to embed custom SAPUI5 applications in SAP Screen Personas flavors, using HTML inline frames and the Cross-Origin Resource Sharing mechanism for data exchange.

The SAP Screen Personas script editor’s primary purpose is to enable input automation by recording or manually defining user interaction sequences using the SAP Screen Personas scripting API and JavaScript. It is not optimized for developing and maintaining custom SAPUI5 code, leading to many customer requests to improve the development experience.

With the SAP Screen Personas 3.0 Support Pack 12 release, we have addressed custom development improvement requests by decoupling SAPUI5 development from SAP Screen Personas scripts with a feature called SAPUI5 applets. The SAPUI5 applets feature is a continuation of SAP Fiori enablement for classic SAP GUI screens rendered with Slipstream Engine.

The purpose of this article is to outline technical considerations when using the SAPUI5 applets feature.

Introducing the SAPUI5 Applets

An SAPUI5 applet is an SAPUI5 SAP Fiori application that is entirely developed, tested, and maintained using appropriate tooling — such as Visual Studio Code, SAP Business Application Studio, or SAP WebIDE — and then deployed on an SAP back-end system and consumed from Slipstream Engine flavors in a single page application environment. Examples of applets could be an SAP Fiori table, a chart, or even a set of services without any user interface (UI).

Once you insert an applet into your flavor, the SAP Screen Personas scripting API can be used to exchange data between SAP GUI runtime and the SAPUI5 applet’s view model.

Some of the advantages of using the SAPUI5 Applets feature over the classic approach that leverages SAPUI5 code directly in flavor scripts include:

  • Separation of user input automation from custom SAPUI5 development
  • Access to a dedicated SAPUI5 integrated development environment that can be used for custom SAP Fiori development by following SAP-provided best practices
  • Improved maintainability, testability, modularity, supportability, and upgradability
  • Better performance, since the single page application concept means that the SAPUI5 framework is not required to be loaded and initialized in HTML inline frames
  • Simple integration and lifecycle management, since SAPUI5 applet components are managed by Slipstream Engine

The high-level process for creating an SAPUI5 applet and consuming it in an SAP Screen Personas flavor is as follows:

  1. Define the integration requirements, including the data model and communication means between the SAP GUI runtime and the SAPUI5 applet component.
  2. Develop the SAPUI5 applet using the SAPUI5 SAP Fiori application project templates and development best practices. The SAPUI5 applet must follow the standard SAP Fiori Model-view-controller application file structure.
  3. Consider software engineering best practices, such as configuration management and quality assurance .
  4. Deploy the SAPUI5 applet on an SAP NetWeaver Application Server ABAP back-end system using the SAPUI5 ABAP Repository by following the standard SAPUI5 SAP Fiori on-premise deployment process.
  5. Create an allowlist entry to enable the deployed SAPUI5 applet to be inserted from the SAP Screen Personas flavor editor. For security reasons, exercise caution and only enable SAPUI5 applets that you trust.
  6. Insert the enabled SAPUI5 applet into your SAP Screen Personas flavor
  7. Bind SAPUI5 applets with SAP GUI runtime by creating an SAP Screen Personas script that acts as an interface adapter.

To better illustrate the SAPUI5 applets feature, let’s look at an example scenario.

Example Scenario

In this example, the classic SAP GUI transaction IW29 Display Notifications, rendered in Slipstream Engine, will be customized by replacing the standard grid control with a responsive SAPUI5 SAP Fiori table to significantly improve its functionality on mobile devices.

The high-level business requirements are as follows:

  • Ensure that the list of notifications is responsive and automatically adapts to mobile device screens.
  • Implement searching, sorting, and filtering capabilities that are easy to use from mobile devices.
  • Launch transaction IW53 Display Service Notification when users select a notification row. The selected ID should be prefilled in IW53 based on the row selected in IW29.
  • IW53 should be already customized using SAP Screen Personas and Slipstream Engine, following the design of an SAP Fiori object page.

To implement these requirements, we will use the SAP Screen Personas 3.0 SP12 SAPUI5 applets feature to replace the IW29 GuiGridView control with an SAPUI5 sap.m.Table control.

Prerequisites

  1. The SAPUI5 ABAP repository and the ABAP back-end infrastructure set-up must be completed.
  2. An ABAP development package must be created for the SAPUI5 application deployment.
  3. SAP Screen Personas 3.0 Support Pack 12 is required.
  4. SAPUI5 Development IDE of choice needs to be installed and configured.
  5. (Optional) UI5 Tooling can be installed
  6. The necessary PFCG roles to deploy new SAPUI5 applications and access SAP Screen Personas admin transaction are required.
  7. To simplify date sorting, consider setting the date format to MM/DD/YYYY in SU01.
  8. The layout of transaction IW29 should be configured to display the following columns:
Table 1. IW29 Notification List Visible Columns
Column Name Column Technical name
Notification QMNUM
Notification Type QMART
Notification Date QMDAT
Priority Text PRIOKX
Priority PRIOK
Description QMTXT
Equipment EQUNR
Malfunction Start AUSVN

Figure%201%20Required%20IW29%20Grid%20Layout%20Settings

Figure 1. Required IW29 Grid Layout Settings

Defining the Data Model and Interfaces

The SAPUI5 applet featured in the demo scenario integrates with SAP GUI runtime in the following ways:

  • It consumes the notification data directly from the IW29 transaction rendered in Slipstream Engine through the common data model.
  • It enables the attachment of an SAP Screen Personas script to the SAPUI5 table row selection event by exposing an external interface.

Data Model

The SAPUI5 applet will leverage the SAPUI5 data-binding to render the dynamic notifications list based on the data structure maintained in the sap.ui.model.json.JSONModel instance.

Here is the JSON data structure target pattern for the demo scenario:

{
    "NotificationCollection": [{
        "QMNUM": "<String: Notification #1 ID value>",
        "QMART": "<String: Notification #1 Type value>",
        "QMDAT": "<String: Notification #1 Date value>",
        "PRIOKX": "<String: Notification #1 Priority text value>",
        "PRIOK": "<String: Notification #1 Priority value>",
        "QMTXT": "<String: Notification #1 Description value>",
        "EQUNR": "<String: Notification #1 Equipment ID value>",
        "AUSVN": "<String: Notification #1 Malfunction Start Date value>"
    }, {
        "QMNUM": "<String: Notification #n ID value>",
        "QMART": "<String: Notification #n Type value>",
        "QMDAT": "<String: Notification #n Date value>",
        "PRIOKX": "<String: Notification #n Priority text value>",
        "PRIOK": "<String: Notification #n Priority value>",
        "QMTXT": "<String: Notification #n Description value>",
        "EQUNR": "<String: Notification #n Equipment ID value>",
        "AUSVN": "<String: Notification #n Malfunction Start Date value>"
    }]
}

Each object in the data structure example represents a single table row and the JSON object keys match the technical column names of the IW29 table layout; therefore simplifying the SAP Screen Personas adapter script, since data mapping functionality is not needed.

The SAP Screen Personas script will set the SAPUI5 applet initial data by:

  • Traversing the original IW29 table and preparing the data structure object according to the agreed format
  • Retrieving the SAPUI5 applet view model instance during the flavor load using the Scripting API
  • Setting the created data structure to the SAPUI5 applet’s view model instance and therefore triggering the automatic data-binding update

External Interface

As per the business requirements, the user’s row selection event must be captured by an SAP Screen Personas script that:

  • takes the selected notification ID,
  • switches the transaction from IW29 to IW53
  • skips the notification selection screen by automatically prefilling the selected notification ID

To implement the requirement, the SAPUI5 Applet will feature a sap/ui/core/service/Service class extension that defines an external interface:

  • attachSelect(Function: Function, Object: Listener):void – Enables the attachment of a JavaScript function instance that is called when users select a table row
  • detachSelect(Function: Function, Object: Listener):void – Detaches the external handler function from the table row selection event
  • setSelectedRowById (String sNotificationId):void – Programmatically sets the selected table row by providing the notification ID as an input

It is crucial to mention that the SAPUI5 applets integration with classic web enabled SAP GUI transactions requires the SAP Screen Personas Scripting Engine API to be used as an interface adapter that translates SAPUI5 applet functionality to SAP GUI runtime. When evaluating feasibility of implementing SAPUI5 applets, always consider the SAP Screen Personas scripting API to ensure that such an adapter for the desired functionality can be implemented through the means of SAP Screen Personas scripts.

See Binding the SAP GUI runtime with the SAPUI5 applet’s data model for more information on how to consume the data model and the external SAPUI5 applet interface from an SAP Screen Personas script to enable seamless integration between custom functionality and the IW29 runtime.

Building the SAPUI5 Applet

The SAPUI5 applet development process for the demo scenario will require the following steps:

  1. Creating the SAPUI5 application project structure
  2. Defining the project configuration in the manifest.json file
  3. Implementing the Component.js file
  4. Defining the external component interface by implementing the SelectionService class
  5. Implementing the SAPUI5 applet XML view
  6. Implementing the SAPUI5 controller for the view

Creating the Initial SAPUI5 Application Project Structure

  1. Decide on the configuration management solution. Git will be used here for demonstration purposes
  2. Create the project folder iw29-mobile-table
  3. Initialize the SAPUI5 Project structure. UI5 tooling and a Yeoman Easy-ui5 generator can be used for this. Create or generate the following empty SAPUI5 SAP Fiori application project files:

iw29-mobile-table/uimodule/webapp/

    • Component.js
    • manifest.json
    • Formatter.js
    • SelectionService.js
    • view/
      • Table.view.xml
    • controller/
      • Table.controller.js
    • fragments/
      • Filter.fragment.xml
      • Sort.fragment.xml

Figure%202.%20SAPUI5%20Applet%20Project%20Structure

Figure 2. SAPUI5 Applet Project Structure

The SAPUI5 applet source code can be downloaded from GitHub here.

Defining the project configuration in the manifest.json file

Open the iw29-mobile-table/uimodule/webapp/manifest.json file and insert the following JSON source code:

{
  "_version": "1.0.0",
  "sap.app": {
    "id": "iw29mobiletable",
    "type": "application",
    "applicationVersion": {
      "version": "1.0.0"
    },
    "title": "{{appTitle}}",
    "description": "{{appDescription}}"
  },
  "sap.ui": {
    "technology": "UI5",
    "icons": {
      "icon": "",
      "favIcon": "",
      "phone": "",
      "phone@2": "",
      "tablet": "",
      "tablet@2": ""
    },
    "deviceTypes": {
      "desktop": true,
      "tablet": true,
      "phone": true
    }
  },
  "sap.ui5": {
    "flexEnabled": false,
    "rootView": {
      "viewName": "iw29mobiletable.view.Table",
      "type": "XML",
      "async": true,
      "id": "Table"
    },
    "dependencies": {
      "minUI5Version": "1.65.6",
      "libs": {
        "sap.ui.layout": {},
        "sap.ui.core": {},
        "sap.m": {},
        "sap.ndc": {}
      }
    },
    "contentDensities": {
      "compact": true,
      "cozy": true
    },
    "models": {
      "DATA": {
        "type": "sap.ui.model.json.JSONModel",
      "dataSource": "test_model"  
      }
    },
    "services": {
      "SelectionService": {
        "factoryName": "iw29mobiletable.SelectionService"
      }
    }
  }
}

Implementing the Component.js file

Open the iw29-mobile-table/uimodule/webapp/Component.js file and insert the following JavaScript source code:

sap.ui.define([
  "sap/ui/core/UIComponent",
  "iw29mobiletable/SelectionService"
], function (UIComponent, SelectionService) {
  "use strict";

  return UIComponent.extend("iw29mobiletable.Component", {

    metadata: {
      manifest: "json"
    },

    /**
     * The component is initialized by UI5 automatically during the startup of the app and calls the init method once.
     * @public
     * @override
     */
    init: function () {
      // call the base component's init function
      UIComponent.prototype.init.apply(this, arguments);
    },
    /*
    * @override
    * Returns new Service instance that an implements external API inteface
    */
    getService : function(sServiceName) {
      if(sServiceName === "SelectionService"){
        return new SelectionService({
          scopeObject: this,
          scopeType: "component"
        });
      }
      return null;
    }
  });
});   

Defining the external component interface by implementing the SelectionService class

Open the iw29-mobile-table/uimodule/webapp/SelectionService.js file and insert the following JavaScript source code:

sap.ui.define([
    "sap/ui/core/service/Service",
    "sap/ui/core/service/ServiceFactory",
    "sap/ui/core/service/ServiceFactoryRegistry"
], function (Service, ServiceFactory, ServiceFactoryRegistry) {
    "use strict";
    var oSelectionService = Service.extend("iw29mobiletable.SelectionService", {
        
        _oEventProvider : null,
        _oEventBus : null,
        
        init: function () {
            ServiceFactoryRegistry.register("iw29mobiletable.SelectionService", new ServiceFactory(this.getInterface()));
            this._oEventProvider = new sap.ui.base.EventProvider();
            this._oEventBus = sap.ui.getCore().getEventBus();
            //The "select" event is published by the iw29mobiletable.controller.Table controller instance once a table row has been selected
            this._oEventBus.subscribe("iw29mobiletable", "select", this._onSelect, this);
        },
        /*
         * @private
         * @param {String} sChannel
         * @param {String} sId
         * @param {Object} oData
         * The method propogates the "select" event fired by the iw29mobiletable.controller.Table instance
         */
        _onSelect : function(sChannel, sId, oData) {
            this._oEventProvider.fireEvent("select", {"id" : oData.id});
        },
        
        /*
         * @param {Function} fnFuntion
         * @param {Object} oListener
         * Attach external handler function to the table row selection event
         */
        attachSelect : function(fnFuntion, oListener){
            this._oEventProvider.attachEvent("select", null, fnFuntion, oListener);
        },
        
        /*
         * @param {Function} fnFuntion
         * @param {Object} oListener
         * Detach external handler function from the table row selection event
         */
        detachSelect : function(fnFuntion, oListener){
            this._oEventProvider.detachEvent("select", fnFuntion, oListener);
        },
        
        /*
         * @param {String} sNotificationId
         * Selects table row by notification id
         */
        setSelectedRowById : function(sNotificationId){
            this._oEventBus.publish("iw29mobiletable", "selectRow", {
                "id": sNotificationId
            });
        }
    });

    return oSelectionService;

});

Implementing the SAPUI5 Applet XML view

Open the iw29-mobile-table/uimodule/webapp/view/Table.view.xml file and insert the following XML source code:

<mvc:View controllerName="iw29mobiletable.controller.Table" xmlns:mvc="sap.ui.core.mvc" xmlns:core="sap.ui.core" xmlns="sap.m">
    <Table id="idNotificationsTable" inset="false" mode="SingleSelectMaster" selectionChange="onSelect"
        items="{ path: 'DATA>/NotificationCollection', sorter: { path: 'Name' } }">
        <headerToolbar>
            <OverflowToolbar>
                <content>
                    <Title text="Notifications" level="H2"/>
                    <ToolbarSpacer/>
                    <SearchField id="searchField" search="onSearch" placeholder="Search by Equipment" width="auto"></SearchField>
                    <Button tooltip="Group" icon="sap-icon://sort" press="onSort"/>
                    <Button tooltip="Filter" icon="sap-icon://filter" press="onFilter"/>
                </content>
            </OverflowToolbar>
        </headerToolbar>
        <columns>
            <Column minScreenWidth="Tablet" demandPopin="true" hAlign="Center">
                <Text text="Notification"/>
            </Column>
            <Column minScreenWidth="Tablet" hAlign="Center">
                <Text text="Notification Date"/>
            </Column>
            <Column minScreenWidth="Tablet" demandPopin="true" hAlign="Center">
                <Text text="Description"/>
            </Column>
            <Column minScreenWidth="Tablet" demandPopin="true" hAlign="Center">
                <Text text="Equipment"/>
            </Column>
            <Column demandPopin="true" hAlign="Center">
                <Text text="Malfunction Start Date"/>
            </Column>
            <Column demandPopin="true" hAlign="Center">
                <Text text="Priority"/>
            </Column>
        </columns>
        <items>
            <ColumnListItem>
                <cells>
                    <ObjectIdentifier title="{DATA>QMNUM}" text="{DATA>QMART}"/>
                    <Text
                        text="{ path: 'DATA>QMDAT', type: 'sap.ui.model.type.Date', formatOptions : { pattern : 'MM/dd/yyyy', source : { pattern : 'MM/dd/yyyy' } } }"/>
                    <Text text="{DATA>QMTXT}"/>
                    <Text text="{DATA>EQUNR}"/>
                    <Text
                        text="{ path: 'DATA>AUSVN', type: 'sap.ui.model.type.Date', formatOptions : { pattern : 'MM/dd/yyyy', source : { pattern : 'MM/dd/yyyy' } } }"/>
                    <ObjectStatus text="{DATA>PRIOKX}" state="{path : 'DATA>PRIOK', formatter : 'iw29mobiletable.Formatter.priority'}"/>
                </cells>
            </ColumnListItem>
        </items>
    </Table>
</mvc:View>

Implementing the SAPUI5 controller for the view

Open the iw29-mobile-table/uimodule/webapp/Formatter.js file and insert the following JavaScript source code:

sap.ui.define(function() {
    "use strict";
    var Formatter = {
        priority :  function (sPriority) {
            switch(sPriority){
                case "3":
                    return "Warning";
                case "2":
                case "1":
                    return "Error";
                default:
                    return "None";
            }
        }
    };
    return Formatter;
}, true);

Open the iw29-mobile-table/uimodule/webapp/fragments/Filter.fragment.xml file and insert the following XML source code:

<core:FragmentDefinition xmlns="sap.m" xmlns:core="sap.ui.core">
    <ViewSettingsDialog confirm="onFilterConfirmed">
        <filterItems>
            <ViewSettingsFilterItem text="Type" key="QMART" multiSelect="true">
                <items>
                    <ViewSettingsItem text="M2 - Malfunction report" key="QMART___EQ___M2"/>
                    <ViewSettingsItem text="S2 - Service request" key="QMART___EQ___S2"/>
                    <ViewSettingsItem text="S3 - Activity report" key="QMART___EQ___S3"/>
                </items>
            </ViewSettingsFilterItem>
            <ViewSettingsFilterItem text="Priority" key="PRIOK" multiSelect="true">
                <items>
                    <ViewSettingsItem text="1-Very high" key="PRIOK___EQ___1"/>
                    <ViewSettingsItem text="2-High" key="PRIOK___EQ___2"/>
                    <ViewSettingsItem text="3-Medium" key="PRIOK___EQ___3"/>
                    <ViewSettingsItem text="4-Low" key="PRIOK___EQ___4"/>
                </items>
            </ViewSettingsFilterItem>
            <ViewSettingsFilterItem text="Notification Date" key="QMDAT" multiSelect="false">
                <items>
                    <ViewSettingsItem text="Today" key="QMDAT___EQ___TODAY"/>
                    <ViewSettingsItem text="Within a month" key="QMDAT___BT___TODAY___MONTH"/>
                    <ViewSettingsItem text="Older than a month" key="QMDAT___LT___MONTH"/>
                </items>
            </ViewSettingsFilterItem>
            <ViewSettingsFilterItem text="Malfunction Date" key="AUSVN" multiSelect="false">
                <items>
                    <ViewSettingsItem text="Today" key="AUSVN___EQ___TODAY"/>
                    <ViewSettingsItem text="Within a month" key="AUSVN___BT___TODAY___MONTH"/>
                    <ViewSettingsItem text="Older than a month" key="AUSVN___LT___MONTH"/>
                </items>
            </ViewSettingsFilterItem>
        </filterItems>
    </ViewSettingsDialog>
</core:FragmentDefinition>

Open the iw29-mobile-table/uimodule/webapp/fragments/Sort.fragment.xml file and insert the following XML source code:

<core:FragmentDefinition
    xmlns="sap.m"
    xmlns:core="sap.ui.core">
    <ViewSettingsDialog
        confirm="onSortConfirm">
        <sortItems>
            <ViewSettingsItem text="Notification Date" key="QMDAT" selected="true" />
            <ViewSettingsItem text="Equipment" key="EQUNR" />
            <ViewSettingsItem text="Malfunction Start Date" key="AUSVN" />
            <ViewSettingsItem text="Priority" key="PRIOKX" />
        </sortItems>
    </ViewSettingsDialog>
</core:FragmentDefinition>

Open the iw29-mobile-table/uimodule/webapp/controller/Table.controller.js file and insert the following JavaScript source code:

sap.ui.define([
    "/../Formatter",
    "sap/ui/core/mvc/Controller",
    "sap/ui/model/json/JSONModel",
    "sap/ui/model/Filter",
    "sap/ui/model/FilterOperator",
    "sap/ui/model/Sorter"

], function (Formatter, Controller, JSONModel, Filter, FilterOperator, Sorter, BarcodeScanner) {
    "use strict";
    var TableController = Controller.extend("iw29mobiletable.controller.Table", {
        _oEventBus: null,
        _oFilterDialog : null,
        _oSortDialog : null,
        _oTable : null,
        
        onInit: function () {
            this._oEventBus = sap.ui.getCore().getEventBus();
            this._oEventBus.subscribe("iw29mobiletable", "selectRow", this.onNotificationSelect, this);
            this._oTable = this.byId("idNotificationsTable");
        },
        
        /*
         * Table row selection handler
         * @param {Object} oEvent
         */
        onSelect: function (oEvent) {
            var oSelectedListItem = oEvent.getParameter("listItem"),
                oNotificationCell = oSelectedListItem.getCells()[0],
                sNotificationId = oNotificationCell.getTitle();
            this._oEventBus.publish("iw29mobiletable", "select", {
                "id": sNotificationId
            });
        },
        
        /*
         * @param {String} sChannel
         * @param {String} sId
         * @param {Object} oData
         * Handles Row selection request
         */
        onNotificationSelect: function (sChannel, sId, oData) {
            var sNotificationId = oData.id;
            var sTitle;
            var oItem;
            var aTableItems = this._oTable.getItems();
            var i;
            for(i = 0; i < aTableItems.length; i++){
                oItem = aTableItems[i];
                sTitle = oItem.getCells()[0].getTitle();
                if(sTitle === sNotificationId){
                    this._oTable.setSelectedItem(oItem);
                    oItem.focus();
                    return;
                }
            }
            this._oTable.removeSelections(true);
        },
        
        /*
         * Table search handler
         * @param {Object} oEvent
         */
        onSearch: function (oEvent) {
            var sQuery,
                oBindings = this._oTable.getBinding("items");
            if (oEvent.getParameters().clearButtonPressed) {
                oBindings.filter("");
            } else {
                sQuery = oEvent.getParameter("query");
                if (sQuery && sQuery.length > 0) {
                    oBindings.filter([new Filter("EQUNR", FilterOperator.Contains, sQuery)]);
                }
            }
        },
        
        /*
         * Sort button press handler
         */
        onSort: function(){
            if (!this._oSortDialog) {
                this._oSortDialog = sap.ui.xmlfragment("iw29mobiletable.fragments.Sort", this);
            }
            this._oSortDialog.open();
        },
        
        /*
         * Filter button press handler
         */
        onFilter: function(){
            if (!this._oFilterDialog) {
                this._oFilterDialog = sap.ui.xmlfragment("iw29mobiletable.fragments.Filter", this);
            }
            this._oFilterDialog.open();
        },
        
        /*
         * Handles table sorting
         * @param {Object} oEvent
         */
        onSortConfirm : function(oEvent){
            var mParams = oEvent.getParameters(),
                bDescending = mParams.sortDescending,
                sPath = mParams.sortItem.getKey(),
                oSorter;
            //A custom sorter is required for dates
            if(sPath === "QMDAT" || sPath === "AUSVN"){
                oSorter = new Sorter(sPath, bDescending, undefined, this._compareDates);
            }else{
                oSorter = new Sorter(sPath, bDescending);
            }
            
            this._oTable.getBinding("items").sort([oSorter]);
        },
        
        /**
         * Compares two date strings
         * @param {String} sDateX
         * @param {String} sDateY
         * @return {Integer} [-1;1] 
         */
        _compareDates : function(sDateX, sDateY){
            var oDateX = new Date(sDateX),
                oDateY = new Date(sDateY);
                
            if(oDateX < oDateY || !oDateY){
                return -1;
            }
    
            if(oDateX > oDateY || !oDateX){
                return 1;
            }
            
            return 0;
        },
        /**
         * Converts date constants into dynamic date strings
         * @param {String} sValue
         * @return {String}
         */ 
        _convertDate : function(sValue){
            var oDate = new Date();
            switch(sValue){
                case "TODAY" :
                    return oDate.toDateString();
                case "MONTH" :
                    oDate.setMonth(oDate.getMonth() - 1);
                    return oDate.toDateString();
                default :
                    return sValue;
            }
        },
        
        /*
         * Handles table filtering
         * @param {Object} oEvent
         */
        onFilterConfirmed : function(oEvent){
            var mParams = oEvent.getParameters(),
                oBinding = this._oTable.getBinding("items"),
                aFilters = [];
            mParams.filterItems.forEach(function(oItem) {
                var aSplit = oItem.getKey().split("___"),
                    sPath = aSplit[0],
                    sOperator = aSplit[1],
                    sValue1 = aSplit[2],
                    sValue2 = aSplit[3],
                    oFilter = new Filter(sPath, sOperator, sValue1, sValue2);
                    //Custom filter settings for dates
                    if(sPath === "AUSVN" || sPath === "QMDAT"){
                        oFilter.oValue1 = this._convertDate(sValue1);
                        oFilter.sValue2 = this._convertDate(sValue2);
                        oFilter.fnCompare = this._compareDates;
                    }
                aFilters.push(oFilter);
            }.bind(this));
            // apply filter settings
            oBinding.filter(aFilters);
        }
    });

    return TableController;
});

Deploying the SAPUI5 Applet

To deploy the finished SAPUI5 application in the SAPUI5 ABAP repository, simply follow the documentation here. For this demonstration, the SAPUI5 applet has been deployed in PERSONAS_TEST_DEMOS package with the name IW29_TABLE assigned.

Figure%203.%20Executing%20the%20/UI5/UI5_REPOSITORY_LOAD%20report%20to%20deploy%20the%20SAPUI5%20Applet

Figure 3. Executing the /UI5/UI5_REPOSITORY_LOAD report to deploy the SAPUI5 Applet

Creating an Allowlist Entry

To insert and consume the deployed SAPUI5 applet in SAP Screen Personas flavors rendered in Slipstream Engine, an allowlist entry needs to be created. For security reasons, please only enable third-party SAPUI5 applications that are verified and trusted.

To create an allow list entry:

  1. Navigate to transaction /n/PERSONAS/ADMIN
  2. Select SAPUI5 Applets under the Whitelists option
  3. Press Change and then press New Entry
  4. Enter the SAPUI5 applet Name and Package specified during the deployment. It is possible to use regular expressions
  5. Save changes

Figure%204.%20Creating%20Allow%20List%20Entries%20for%20SAPUI5%20Applets

Figure 4. Creating Allow List Entries for SAPUI5 Applets

Inserting the SAPUI5 Applet in a Flavor

Once an SAPUI5 applet is permitted, it can be embedded in any web-enabled classic SAP GUI transaction rendered in Slipstream Engine using the flavor editor. For the demo scenario, the IW29 standard GuiGridView control will be replaced with the SAPUI5 applet. To do so:

  1. Navigate to transaction IW29 using Slipstream Engine
  2. Press Execute to skip the filter page
  3. Create a new SAP Screen Personas flavor
  4. Select and hide the original GuiGridView control
  5. Select the GuiUserArea container
  6. Select the Templates tab, then SAPUI5 Applets
  7. Look for the package containing the SAPUI5 applet specified during the deployment
  8. Select the desired SAPUI5 applet component
  9. Check the Stretch to container size option. Doing so will enable the SAPUI5 applet view to take all the available container real estate and dynamically adapt if the container (in this case, GuiUserArea) has been resized, ensuring that the SAPUI5 applet will dynamically adapt its size based on the device screen resolution. 

    Figure%205.%20Inserting%20SAPUI5%20Applet

    Figure 5. Inserting SAPUI5 Applet

  10. Press Done to insert the SAPUI5 applet into the GuiUserArea container
  11. Save changes and exit the flavor editor

Figure%206.%20SAPUI5%20Applet%20Rendered%20in%20IW29%20Runtime

Figure 6. SAPUI5 Applet Rendered in IW29 Runtime

Binding the SAP GUI runtime with the SAPUI5 applet’s data model

The SAPUI5 applet component will now be rendered in IW29 runtime, however the notifications list will be empty. To bind the data between the SAP GUI runtime and the SAPUI5 applet, we can use the SAP Screen Personas Scripting Engine API.

The following functionality is required:

  • Read the original IW29 GuiGridView table in a JavaScript object according to the established data model
  • Wait for the SAPUI5 applet asynchronous initialization, then retrieve the SAPUI5 App Component class instance reference and:
    • Retrieve the SAPUI5 applets view model class instance
    • Set the IW29 runtime data to the SAPUI5 applet view thereby triggering the data-binding update
    • Retrieve the SAPUI5 applet SelectionService class instance to access the external API and attach a JavaScript function that handles the SAPUI5 applet row selection event

The following steps will bind the IW29 runtime with the SAPUI5 applet:

  1. Select the IW29 flavor containing the SAPUI5 applet in the SAP Screen Personas flavor manager and open the script editor.
  2. Create a new script. For example: setAppletData
  3. Insert the following source code.
    // Load table data from screen
    var fnReadModelData = function (){
    	var oSelectedTable = session.findById("wnd[0]/usr/cntlGRID1/shellcont/shell");
    	var aColumns = oSelectedTable.columns;
    	var aContents = [];
    	var sSAPColumnName;
    	var iTopRow, iRowIdx, oRow, i;
    	if (oSelectedTable.rowCount > 0) {
    		oSelectedTable.firstVisibleRow = 0;
    		iTopRow = oSelectedTable.visibleRowCount - 1;
    		for (iRowIdx = 0; iRowIdx < oSelectedTable.rowCount; iRowIdx++) {
    			oRow = {};
    			if (iRowIdx > iTopRow) {
    				if (iTopRow + oSelectedTable.visibleRowCount > oSelectedTable.rowCount) {
    					oSelectedTable.firstVisibleRow = oSelectedTable.rowCount - oSelectedTable.visibleRowCount;
    				} else {
    					oSelectedTable.firstVisibleRow = iTopRow + 1;
    				}
    				iTopRow += oSelectedTable.visibleRowCount;
    			}
    			for (i = 0; i < aColumns.length; i++) {
    				sSAPColumnName = aColumns.elementAt(i).name;
    				oRow[sSAPColumnName] = oSelectedTable.getCellValue(iRowIdx, sSAPColumnName);
    			}
    	
    			aContents.push(oRow);
    		}
    	}
    	return aContents;
    };
    
    
    var oComponent = session.findById("<PLACE YOUR APPLET ID HERE>").getComponent();
    
    oComponent.getService("SelectionService").attachSelect(function(oEvent){
    		session.utils.put("SELECTED_NOTIFICATION", oEvent.getParameter("id"));
            
    }.bind(this));
    
    //Set the SAPUI5 Applet view model data 
    oViewModel = oComponent.getModel("DATA");
    oViewModel.setData({"NotificationCollection" : fnReadModelData()});
    
  4. Use the Object Selector to select the SAPUI5 applet control and replace the <PLACE YOUR APPLET ID HERE> placeholder featured in the code snippet above with the actual SAPUI5 applet control ID.
  5. Save the script and close the script editor.

Once the interface adapter script is finished, it is possible to bind the script with the SAPUI5 applet component load event in the flavor editor:

  1. Edit the flavor
  2. Select the SAPUI5 applet control
  3. Open the Script Assignments dialog (Insert Tab > Script Events)
  4. Assign the created script to the onAppletComponentLoad event
  5. Save the changes, exit the flavor editor, and reload the flavor
  6. The data binding between the SAPUI5 applet and the IW29 runtime is now complete

Figure%207.%20SAPUI5%20Applet%20Displaying%20Notifications%20from%20IW29

Figure 7. SAPUI5 Applet Displaying Notifications from IW29

Further Considerations

  • For security reasons, always exercise caution and assess potential risks when embedding third-party functionality through the SAP Screen Personas scripting engine and the SAPUI5 applets feature.
  • SAPUI5 applets require SAPUI5 runtime, therefore only Slipstream Engine supports the feature.
  • The SAPUI5 applets feature uses the same SAPUI5 version that is loaded with Slipstream Engine. Make sure your custom applets are compatible with the target SAPUI5 version, especially when SAP Fiori launchpad integration and different SAPUI5 themes are being used. More information is available here.
  • For SAP Fiori launchpad HUB deployment, it is recommended to deploy SAPUI5 applets on the Gateway server and create an allowlist entry for each back-end system hosting Slipstream Engine and consuming SAPUI5 applets.
  • SAPUI5 applets are considered custom development, therefore the SAP Screen Personas team cannot provide technical support or consultation regarding additional functionality enabled through the means of the SAPUI5 applets feature.

Summary

The SAP Screen Personas team strives to deliver the right tools and functionality to customers who rely on classic SAP GUI transactions and are looking for ways to enable the SAP Fiori user experience for their scenarios.

The mobile-ready rendering of SAP GUI transactions in Slipstream Engine supports SAPUI5 SAP Fiori themes, accessibility standards, and seamless SAP Fiori launchpad integration. The SAPUI5 applets feature is a logical continuation of the SAP Fiori enablement journey and provides customers a way to seamlessly integrate custom SAPUI5 functionality such as tables, charts, forms and lists into SAP Screen Personas flavors rendered in Slipstream Engine.

The SAP Screen Persons team thanks everyone who shared feature requests and helped to shape the SAPUI5 Applets feature!

On behalf of SAP UX Engineering and the SAP Screen Personas team,

Conrad Bernal, Tamas Hoznek, Peter Spielvogel and Krists Magons

Further Reading

 

12 Comments
You must be Logged on to comment or reply to a post.
  • Hi Krists,

    This is really exciting stuff!!

    When I tried to implement this following this blog , I am experiencing one issue.

    I have deployed my UI5 Applet as per you suggested , then whitelisted the SAPUI5 applet but when I open my flavor editor I am not able to find it under Templates==>SAPUI5 Applet. Getting a blank screen showing this "No SAPUI5 Applets Loaded". I am on SP12 but my flavor got created earlier. What might be the issue causing this?

    (I have already cleared my server Cache here -  /n/personas/health but it does not help)

    Thanks for your time.

    Regards,

    Sunit

     

    • Hi Sunit,

       

      Are you deploying your SAPUI5 Fiori application to the same ABAP system that has the SAP Screen Personas SP12 installed?

      Is the application correctly deployed? Can you access the Component.js file directly through the SICF service URL?

      Does the whitelist entry match the package and the app name correctly?

       

      We can set-up a screen sharing call next week, my e-mail address is: krists.magons@sap.com

       

      Thank you!

       

      Best regards,

      Krists Magons

      SAP Screen Personas Dev team

       

       

       

      • Thanks a lot Krists, its perfectly working for me now. One follow up question -

        Can this be done using Smart table? Will personas support this functionality if I use smart table ?

         

        Thank you,

        Best Regards,

        Sunit

  • Hi Sunit,

    As long as the SAPUI5 version that is loaded with SAP Screen Personas is compatible with your SAPUI5 Applet and there is a SAP Screen Personas Scripting API for your integration scenario you can build your custom applet using any SAPUI5 control including smart tables.

    Thanks,
    Krists

  • Hi Krists,

    we are working  on a project to use ScreenPersonas including UI5 Applets. As we know to be able to use Slipstream the fopllowing services needs to be activated:
           /default_host/sap/bc/se/
           /default_host/sap/bc/se/m
          /default_host/sap/bc/se/proxy (must be activated for integration of Slipstream Engine into the Fiori Launchpad)
          /default_host/sap/bc/se/rg

    but we are still facing the issue that we are not able to create an ui5 applet in the web IDE.

     

     

    When we try to test the sliptstream engine we get this

    but after several seconds it disapears and we get a blank page.

    Is there something we missed to enable/customize?

    Is there any documentation how to setup slipstream correctly?

     

    Would be glad, if you can give us some hints, how to solve this issue.

     

    Warm regards from Germany

     

    Stefan

     

     

     

     

     

     

    • Hello Stefan,

      To resolve the blank page problem when logging on via Slipstream Engine, create a support incident for component BC-PER, provide an open connection to your system, and maintain credentials in the secure area with development level access assigned. The system should preferably be you development instance, so the fix can be transported to other systems having the same blank screen problem with SE.

      As for the missing SAPUI5 Applet function, that is a Slipstream-exclusive feature, and since SE doesn't work for you yet, I think your screen shot is taken via webgui. Also, you must be on SP12 for this to work.

      Regards,
      Tamas.

  • Hi Krists,

    This is really amazing!!

    Is there anyway you can show us on how you uploaded the SAPUI5 to backend? I've read the instruction and I still can wrap my head around it. I also look at the documentation in GitHub and I couldn't see anything specific in regard to loading the applet to backend.

    Thank you.

    • Hello Cyrus, if you are using Visual Studio code and your On Premise ABAP ADT tools are accessible from your desktop by HTTP requests, there is a great tool delivered by the VS Code Fiori extensions that is already available in you VS code Terminal.

      Open an Integrated Terminal from your project folder in VS Code. Then type the command:

      npx fiori add deploy-config

      Follow the wizard. At the end, a new ui5-deploy.yaml file will be created. Be aware to define your credentials in the deployment file. You can find further information on this by reading this blog. I personaly use environment variables for this purpose in an .env file also located at my project root folder.

      Then, still in the terminal type the command:

      npm run deploy

      The deployment to your ABAP system starts automatically.

       

  • Hi Krists, This is a great tutorial, I learned a lot with this sample. 🙂

    I have implemented it in the SAP EPM demo transaction SEPM_SO.

    It works fine, Except for one thing. I don't see in your sample when the detachSelect should be called. I think it should be in a Personas script. But I don't see it in your sample.

    As a consequence, until I leave the Slipstream Session, when i arrive on the SEPM_SO falvor where the SAPUI5 applet is loaded, a new attachSelect is called and as a consequence, when I click on SAPUI5 table row, the event is processed n times depending on the number of times I come back to the screen inside the flavor whre the SAPUI5 applet is implemented and re-loaded. I tried to find a workaround, even by calling the detachSelect method before calling the attachSelect without success.

    When and how the detachSelect should be called ?

     

    Regards

    FX

    • Hi François Xavier,

      Thank you very much for your feedback!

      The lifecycle of an SAPUI5 applet component instance is associated and managed with the DYNPRO screen triple: t-code, program, screen. Which means that the instance should be created when you navigate to the screen containing the applet and destroyed when the given screen context changes.
      Therefore, in my example scenario detachSelect is not called because selecting a row in the SAPUI5 Fiori table according to the attached script changes the context to a different transaction (IW29 - > IW53) which destroys the SAPUI5 component instance without needing to manually detach the selection handler.

      If you see that the SAPUI5 applet instance is not correctly destroyed when changing the context (t-code, program, screen), please:

      1) Check the console for any script errors
      2) Make sure you are using the latest SP12 Client version
      3) Create a BC-PER incident ticket, provide steps to reproduce the problem and consider opening an HTTP support connection

      Thank you, and stay safe!

      -Krists

  • Hi Krists!

    thank you very much for this great tutorial it helps us a lot. We are really interested in the SAP Fiori launchpad HUB deployment scenario. We deployed our SAP UI5 applet in the backend system, so when we are integrating in the launchpad we are not accessing the corresponding service at least we include the corresponding entry in Web Dispatcher. Please, can you explain a little further what is means your following comment

    "For SAP Fiori launchpad HUB deployment, it is recommended to deploy SAPUI5 applets on the Gateway server and create an allowlist entry for each back-end system hosting Slipstream Engine and consuming SAPUI5 applets."

    We tried to find any SAP documentation about it but we did not find it

     

    Thanks in advance

    • Hi Dionisio,

      Thank you very much for your feedback!

      From the deployment perspective, SAPUI5 applets are decoupled from SAP Screen Personas flavors and should follow the standard SAPUI5 Fiori application deployment on the front-end server. Doing so would not require to modify the SAP WebDispatcher configuration to load the applet sources from the back-end system.
      However, the SAP Screen Personas Admin transaction allowlist entry matching the applet details is still required to load the applet in SAP Screen Personas runtime.

      At this point, we do not have a documentation article explaining the SAPUI5 applet configuration steps in a HUB setup. Noted in our task backlog for further consideration.

      Have a nice day!

      Best regards,
      Krists Magons