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.
- Install the VS Code from https://code.visualstudio.com/
- Install UI5 CLI globally. For installation and documentation please visit https://sap.github.io/ui5-tooling/
npm install --global @ui5/cli
- 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
- 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
- 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
- Go to your project folder
cd <your_project_name>
- Installs packages, and any packages that it depends on.
npm install
- Inside our project root folder type “code .” and open VS Code. Our project structure is as follows
code .
- 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
- Our project needs dependency, install it via npm i ui5-middleware-simpleproxy.
npm i ui5-middleware-simpleproxy --save-dev
- 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": {} }
- 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/"
- Start the server
ui5 serve --port 8080 --open test/testOvp.html
- 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}}" } ] } } } } }
- 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.
Original Article:
https://blogs.sap.com/2020/05/05/local-development-with-ui5-tooling-and-yeoman-for-s-4hana-on-premise/