Skip to Content
Technical Articles
Author's profile photo Huseyin Demirkale

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.

 

Assigned Tags

      3 Comments
      You must be Logged on to comment or reply to a post.
      Author's profile photo Thorsten Klingbeil
      Thorsten Klingbeil

      Excellent Blog! Thanks Huseyin! ??

      Do you know, if it’s also possible to deploy this app to an ABAP S/4 Backend from UI5 Tooling?

      Author's profile photo Huseyin Demirkale
      Huseyin Demirkale
      Blog Post Author

      Hello Thorsten,

      Thank you. It is possible to deploy. there is a blog post from Florian Pfeffer about it https://blogs.sap.com/2019/11/24/ui5-tooling-custom-task-to-deploy-ui5-sources-to-an-abap-server/ .

       

      Regards.

       

      Author's profile photo Thorsten Klingbeil
      Thorsten Klingbeil

      Thanks a lot Huseyin - this is exactly what I was looking for! ??