Skip to Content
Technical Articles

Local development with UI5 Tooling and Yeoman for S/4HANA On-Premise

In this blog post we will create a sample Overview Page using UI5 tooling and yeoman generator with local development tools. 

Introduction

From the website, UI5 Tooling is described as follows;  An open and modular toolchain to develop state-of-the-art applications based on the UI5 framework. It is open source project based on Node.js which offers a command line interface for efficient development with SAPUI5 framework.

For more information about UI5 Tooling:

And Yeoman we will simplify the creation of OpenUI5 prototypes.

Before starting hands-on part,  I must say that SAP Web IDE and Business Application Studio have lots of wizards, features, deployment options and tools for the developers.

Prerequisites

Feature list

  • Install UI5 Tooling
  • Develope a simple CDS view on S/4HANA or use an existing one
  • Create a simple SAPUI5 Overview page with Yeoman

Hands-on

For this hands-on, you need a couple of things. Obviously, UI5 tooling CLI interface and Yeoman is mandatory. Furthermore, Microsoft’s VS Code is also a crucial part of this exercise.

  1. Install the VS Code from https://code.visualstudio.com/
  2. Install UI5 CLI globally. For installation and documentation please visit https://sap.github.io/ui5-tooling/
    npm install --global  @ui5/cli
  3. Install Yeoman and generator plugin&verify installation. I have created a simple generator for OVP(a simple scaffolding tool that generates overview page). Thanks for the great blog post .
    npm install -g yo ​generator-easy-ovp-ui5
    --
    yo
  4. Now, jump to the Eclipse and create a simple CDS views with annotations, I used @Odata.publish:true annotation for odata service.
    @AbapCatalog.sqlViewName: 'ZZTESTCINV'
    @EndUserText.label: 'Test CDS View for Overview Page'
    @Metadata.ignorePropogatedAnnotations: true
    @AccessControl.authorizationCheck: #CHECK
    @AccessControl.personalData.blocking: #REQUIRED
    @ClientHandling.algorithm: #SESSION_VARIABLE
    @AbapCatalog.compiler.compareFilter: true
    
    @ObjectModel.usageType.serviceQuality: #X
    @ObjectModel.usageType.sizeCategory: #XXL
    @ObjectModel.usageType.dataClass: #MIXED
    
    @VDM.viewType: #CONSUMPTION
    @OData.publish: true
    @UI.chart: [{
        qualifier: 'LinesReason',
        chartType: #COLUMN,
        dimensions: ['CompanyCode'],
        dimensionAttributes: [{
          dimension: 'CompanyCode',
          role: #CATEGORY
        }],
        measures: ['ConvertedAmount'],
        measureAttributes: [{
          measure: 'ConvertedAmount',
          role: #AXIS_1
        }]
    },{
        qualifier: 'Aging',
        chartType: #COLUMN,
        dimensions: ['Days1'],
        dimensionAttributes: [{
          dimension: 'Days1',
          role: #CATEGORY
        }],
        measures: ['ConvertedAmount'],
        measureAttributes: [{
          measure: 'ConvertedAmount',
          role: #AXIS_1
        }]
    }]
    
    define view ZCDS_DDL_TEST_OVP
      with parameters
        @Consumption.hidden: true
        @Environment.systemField: #SYSTEM_DATE
        P_KeyDate         : vdm_v_key_date,
        @Consumption.hidden: true
        @Environment.systemField: #SYSTEM_DATE
        P_TodayDate       : sydate,
        @Consumption.hidden: true
        @Environment.systemField: #SYSTEM_LANGUAGE
        P_Language        : sylangu,
        @Consumption.defaultValue: 'EUR'
        @Consumption.valueHelpDefinition: [{ entity:{ name: 'I_Currency', element: 'Currency' } }]
        P_DisplayCurrency : vdm_v_display_currency
      as select from P_BSEG_COM 
      {
          @UI.selectionField.position:10
          @Consumption.valueHelpDefinition: [{ entity:{ name: 'I_CompanyCodeVH', element: 'CompanyCode' }}]
      key bukrs as CompanyCode,
          @Consumption.filter.hidden: true
          @Consumption.semanticObject: 'Supplier'
          @UI.identification: {
            importance: #HIGH,
            type: #FOR_INTENT_BASED_NAVIGATION,
            semanticObjectAction: 'manageLineItems'
          }
      key belnr as AccountingDocument,
          @Consumption.filter.hidden: true
      key gjahr as FiscalYear,
          @Consumption.filter.hidden: true
      key buzei as AccountingDocumentItem,
          @Consumption.filter.hidden: true
          cast('' as char20) as DataPeriodName,
    
          @ObjectModel.text.element: 'ReasonName'
          @UI.textArrangement: #TEXT_LAST
          zlspr as Reason,
     
          @Consumption.filter.hidden: true
          @ObjectModel.text.element: 'DataPeriodName'
          @UI.textArrangement: #TEXT_ONLY
          @EndUserText.label: 'Days'
          '#3:61-90'  as Days1,
          // ConvertedAmount,
          @EndUserText.label: 'Amount in DC'
          @DefaultAggregation:#SUM
          @Semantics.amount.currencyCode: 'DisplayCurrency'
          @UI.lineItem:[{
           position: 10,
           type: #AS_DATAPOINT
          }]
          @UI.dataPoint:{
            valueFormat:{ numberOfFractionalDigits: 1 }
          }
          cast(
            -cast(
              currency_conversion(
                amount => wrbtr_shl,
                source_currency => h_waers,
                target_currency => $parameters.P_DisplayCurrency,
                exchange_rate_date => $parameters.P_TodayDate
              ) as farp_amount_display_crcy
            ) as farp_amount_display_crcy)
          as ConvertedAmount,
          $parameters.P_DisplayCurrency as DisplayCurrency,
          @UI.selectionField.position:20
          @Consumption.valueHelpDefinition: [{ entity:{ name: 'I_Supplier_VH', element: 'Supplier' } }] 
          lifnr as Supplier 
    } where wrbtr_shl<0 
    


    And register it via tcode : /IWFND/MAINT_SERVICE

     

  5. Create a folder and scaffold your UI5 project.
    1 mkdir ui5tooling
    2 cd ui5toolingg
    3 yo easy-ovp-ui5​
    
      #Enter module name and module namespace
      #Enter CDS view name   ( ZCDS_DDL_TEST_OVP )
      #Enter Project description

  6. Go to your project folder
    cd <your_project_name>
  7.  Installs packages, and any packages that it depends on.
    npm install​
  8. Inside our project root folder type “code .” and open VS Code. Our project structure is as follows
    code .​

  9. Our generated project has ui5.yaml file which has the configuration. It has a project name, project type and version. And also it allows the definition of a path mappings, Defines build, server, and extension configuration information.
    specVersion: '1.0'
    metadata:
      name: SampleOVP
    type: application
    resources:
      configuration:
        propertiesFileSourceEncoding: UTF-8
    builder:
      customTasks:
        - name: webide-extension-task-updateNeoApp
          afterTask: generateVersionInfo
          configuration:
            destDir: dist
            appFolder: webapp
            nameSpace: com.my.org
        - name: webide-extension-task-updateManifestJson
          afterTask: webide-extension-task-updateNeoApp
          configuration:
            appFolder: webapp
            destDir: dist
        - name: webide-extension-task-lint
          afterTask: webide-extension-task-updateManifestJson
          configuration:
            destDir: dist
            appFolder: webapp
            nameSpace: com.my.org
        - name: webide-extension-task-resources
          afterTask: webide-extension-task-lint
          configuration:
            nameSpace: com.my.org​
  10. Our project needs dependency, install it via npm i ui5-middleware-simpleproxy.
    npm i ui5-middleware-simpleproxy​ --save-dev
  11. Open your package.json file in your root project directory and add the dependency
    {
      "name": "SampleOVP",
      "version": "0.0.2",
      "description": "My OVP Page",
      "devDependencies": {
        "@sap/ui5-builder-webide-extension": "1.0.x",
        "@ui5/cli": "1.13.0",
        "ui5-middleware-simpleproxy": "0.2.2"
      },
      "scripts": {
        "build": "ui5 build --clean-dest --include-task=generateManifestBundle generateCachebusterInfo"
      },
      "ui5": {
        "dependencies": [
          "@sap/ui5-builder-webide-extension",
          "ui5-middleware-simpleproxy"
        ]
      },
      "private": "true",
      "dependencies": {}
    }
    ​
  12. Add ui5-middleware-simpleproxy to the ui5.yaml file.

    and replace <host> and <port> with the values of your On-Premise system

    server:
      customMiddleware:
        - name: ui5-middleware-simpleproxy
          afterMiddleware: compression
          mountPath: /resources
          configuration:
            baseUri: "https://sapui5.hana.ondemand.com/1.76.0/resources/"
        - name: ui5-middleware-simpleproxy
          afterMiddleware: compression
          mountPath: /sap
          configuration:
            baseUri: "http://<host>:<port>/sap/"

    the final ui5.yaml is:

    specVersion: '1.0'
    metadata:
      name: SampleOVP
    type: application
    resources:
      configuration:
        propertiesFileSourceEncoding: UTF-8
    builder:
      customTasks:
        - name: webide-extension-task-updateNeoApp
          afterTask: generateVersionInfo
          configuration:
            destDir: dist
            appFolder: webapp
            nameSpace: com.my.org
        - name: webide-extension-task-updateManifestJson
          afterTask: webide-extension-task-updateNeoApp
          configuration:
            appFolder: webapp
            destDir: dist
        - name: webide-extension-task-lint
          afterTask: webide-extension-task-updateManifestJson
          configuration:
            destDir: dist
            appFolder: webapp
            nameSpace: com.my.org
        - name: webide-extension-task-resources
          afterTask: webide-extension-task-lint
          configuration:
            nameSpace: com.my.org
    server:
      customMiddleware:
        - name: ui5-middleware-simpleproxy
          afterMiddleware: compression
          mountPath: /resources
          configuration:
            baseUri: "https://sapui5.hana.ondemand.com/1.76.0/resources/"
        - name: ui5-middleware-simpleproxy
          afterMiddleware: compression
          mountPath: /sap
          configuration:
            baseUri: "http://<host>:<port>/sap/"

     

  13. Start the server
    ui5 serve --port 8080 --open test/testOvp.html
  14. Replace “sap.ovp” in manifest json file.
    with

    {
    	"_version": "1.7.0",
    	"start_url": "start.html",
    	"sap.app": {
    		"id": "SampleOVP",
    		"type": "application",
    		"i18n": "i18n/i18n.properties",
    		"applicationVersion": {
    			"version": "1.0.0"
    		},
    		"title": "{{app_title}}",
    		"description": "{{app_description}}",
    		"dataSources": {
    			"ZCDS_DDL_TEST_OVP_CDS": {
    				"uri": "/sap/opu/odata/sap/ZCDS_DDL_TEST_OVP_CDS/",
    				"type": "OData",
    				"settings": {
    					"odataVersion": "2.0",
    					"annotations": [
    						"ZCDS_DDL_TEST_OVP_CDS_VAN",
    						"annotation"
    					],
    					"localUri": "localService/ZCDS_DDL_TEST_OVP_CDS/metadata.xml"
    				}
    			},
    			"ZCDS_DDL_TEST_OVP_CDS_VAN": {
    				"uri": "/sap/opu/odata/IWFND/CATALOGSERVICE;v=2/Annotations(TechnicalName='ZCDS_DDL_TEST_OVP_CDS_VAN',Version='0001')/$value/",
    				"type": "ODataAnnotation",
    				"settings": {
    					"localUri": "localService/ZCDS_DDL_TEST_OVP_CDS/ZCDS_DDL_TEST_OVP_CDS_VAN.xml"
    				}
    			},
    			"annotation": {
    				"type": "ODataAnnotation",
    				"uri": "annotations/annotation.xml",
    				"settings": {
    					"localUri": "annotations/annotation.xml"
    				}
    			}
    		},
    		"sourceTemplate": {
    			"id": "OVP.smartovptemplate",
    			"version": "1.41.1"
    		}
    	},
    	"sap.ui": {
    		"technology": "UI5",
    		"icons": {
    			"icon": ""
    		},
    		"deviceTypes": {
    			"desktop": true,
    			"tablet": true,
    			"phone": true
    		}
    	},
    	"sap.ui5": {
    		"dependencies": {
    			"minUI5Version": "1.65.6",
    			"libs": {
    				"sap.ovp": {}
    			}
    		},
    		"models": {
    			"i18n": {
    				"type": "sap.ui.model.resource.ResourceModel",
    				"uri": "i18n/i18n.properties"
    			},
    			"@i18n": {
    				"preload": true,
    				"type": "sap.ui.model.resource.ResourceModel",
    				"uri": "i18n/i18n.properties"
    			},
    			"ZCDS_DDL_TEST_OVP_CDS": {
    				"dataSource": "ZCDS_DDL_TEST_OVP_CDS",
    				"settings": {
    					"defaultCountMode": "Inline"
    				}
    			}
    		},
    		"extends": {
    			"extensions": {}
    		},
    		"contentDensities": {
    			"compact": true,
    			"cozy": true
    		}
    	},
    	"sap.ovp": {
    		"considerAnalyticalParameters": true,
    		"containerLayout": "resizable",
    		"enableLiveFilter": false,
    		"globalFilterEntityType": "ZCDS_DDL_TEST_OVPResult",
    		"globalFilterModel": "ZCDS_DDL_TEST_OVP_CDS",
    		"refreshIntervalInMinutes": 15,
    		"showDateInRelativeFormat": false,
    		"cards": {
    
    			"card01_InvoiceChart": {
    				"model": "ZCDS_DDL_TEST_OVP_CDS",
    				"template": "sap.ovp.cards.charts.analytical",
    				"settings": {
    					"customParams": "blockedInvoicesChart",
    					"title": "{{field2}}",
    					"subTitle": "{{field1}}",
    					"entitySet": "ZCDS_DDL_TEST_OVPResults",
    					"valueSelectionInfo": "{{field3}}",
    					"selectionAnnotationPath": "com.sap.vocabularies.UI.v1.SelectionVariant#params",
    					"tabs": [
    						{
    							"chartAnnotationPath": "com.sap.vocabularies.UI.v1.Chart#LinesReason",
    							"dataPointAnnotationPath": "com.sap.vocabularies.UI.v1.DataPoint#ConvertedAmount",
    							"selectionAnnotationPath": "com.sap.vocabularies.UI.v1.SelectionVariant#params",
    							"value": "{{field4}}"
    						}
    					]
    				}
    			}
    
    		}
    	}
    }​
  15. Refresh testOvp page.http://localhost:8080/test/testOVP.html#OVP-display

 

Great Job!

 

Conclusion

In this blog post, We develop a SAPUI5 Overview page  application locally with the UI5 Tooling.

In local development,  editing manifest.json file and annotation.xml file is a bit difficult and SAP WEBIDE has great features about cards and annotations with visual tools like annotation modeler. ).

To summarize

First we installed the required tools for development and did the followings,

Install UI5 tooling and dependencies,

Invoke the Yeoman scaffolding tool,

Edit ui5.yaml file for local proxy

Create a simple CDS view and publish it via /IWFND/MAINT_SERVICE

Edit manifest.json for SAPUI5 cards.

 

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