Technology Blogs by SAP
Learn how to extend and personalize SAP applications. Follow the SAP technology blog for insights into SAP BTP, ABAP, SAP Analytics Cloud, SAP HANA, and more.
cancel
Showing results for 
Search instead for 
Did you mean: 
former_member183924
Active Participant
I like to write some notes down, from my findings how to implement custom tiles with in Portal service on Cloud Foundry (SCP / Cloud Platform). Unfortunately, the current documentation is limited and doesn't show a good end-to-end sample. I only could get it running with a lot of trial and error.

 

Updates


03/27/2020 – added end-to-end sample with one Portal, two custom tiles, one basic UI5 app as target and CAP based service as backend for the charts: https://github.com/citoki/portal-custom-tile

 

This is the result. You'll get a tile, where you can put in your controls of choice. In my sample below, I use two ComparisonMicroChart controls in the left tile and in the right one two HarveyBallMicroChart controls. For the first tile I used a JS-View and for the second a XML-View, just for demonstration. You can choose what fits better to your coding style.


Contents



  • Create a Starter Project

  • Create a Tile App

  • Tile Content

  • Reading Data Continuously

  • Parameterise Custom Tile via FLP Config

  • Code - End-To-End Sample


Create a Starter Project


As a starting point for a new FLP project I like to generate the necessary artefacts by the WebIDE. You can follow the steps described by iris_blog in her detailed blog: SAP Fiori launchpad site with portal service on SAP Cloud Platform

One thing which is crucial to get a minimal project running on CF (Cloud Foundry), is that the custom tile points to an existing UI5 app. That is the target app which will be defined in business app file baCustomTile.json later in this blog.

In my case, I called the target app like this: tutorial.app.one.app1

Create a Tile App


Now let's jump into the code!

For the new custom tile we need a separate UI5 app. There are a lot of ways to scaffold an UI5 app. One thing I can recommend is the Yo-plugin by mariusobert. He started developing the Yo plugin Generator-Easy-UI5. With that plugin you'll be fast on the track.

The only thing you need to add in the UI5 app, is the FLP configuration This tells the Portal service that the app should be handled as a Tile with a specific tileSize of 1x1.
"sap.flp": {
"type": "tile",
"tileSize": "1x1"
},

tileSize currently only supports 1x1 (square tile) and 1x2 (wide rectangle tile).

Fiori Launchpad Configuration


Below you can see the structure we need in the end.
flp/portal-site/CommonDataModel.json
flp/portal-site/business-apps
└── baCustomTile.json
flp/portal-site/i18n
└── i18n.properties

The configuration for the Launchpad (FLP) is done via the flp/portal-site/CommonDataModel.json file. In config path "payload.catalogs.payload.viz" add following object to the viz array:

File: CommonDataModel.json


viz: [{
"id": "tutorial.app.one.app1", // has to be equal to 'appId' wihtin groups
"vizId": "customTile-displayToCustom" // visualization from business app configuration
}]

This tells the FLP runtime, that there is a special visualisation for  app1 available.

In the same file (CommonDataModel.json), we need to set the specific visualisation for the tile. That is done in the object path "payload.groups.payload". Just add the following code there:

File: CommonDataModel.json


"viz": [{
"id": "tutorial.app.one.app1-0-1573035031268",
"appId": "tutorial.app.one.app1",
"vizId": "customTile-displayToCustom" // visualisation ID from business app configuration
}]

The configuration and the target mapping, when clicking on the custom tile has to be made in a specific folder flp/portal-site/business-apps. Just create a JSON file (in my example: baCustomTile.json) with following code:

File: baCustomTile.json


{
"_version": "3.0.0",
"identification": {
"id": "tutorial.app.one.app1", // app which should be opened by the custom tile
"entityType": "businessapp",
"i18n": "i18n/i18n.properties"
},
"payload": {
"visualizations": { // multiple visualisations are possible to be defined here
"customTile-displayToCustom": { // one sample of visualization for our 'app1'
"vizType": "project.namespace.customTile", // type has to be set to the app name of our customTile
"vizConfig": {
"sap.app": {
"title": "{{notifList.tile.title}}",
"subTitle": "{{notifList.tile.subtitle}}"
},
"sap.flp": {
"target": {
"inboundId": "data-display" // inbound config for the target app 'app1'
}
}
}
}
}
}
}

 

And that's it. Just deploy the FLP like normal and the custom tile should appear right there.

Tile Content ?


Please read the Fiori guidelines on tiles, for further details on when to use tiles and which content should be shown.

Do not play videos in their and do not provide any interactive controls like buttons, polls, list. ?

In some use cases the OVP (Overview Page) could be the better choice.

Reading data continuously


Now, for tiles are quickly coming up questions like "How can read the data continuously every X seconds?" Well, you might have seen the dynamic tile. For that you can configure an interval to update the data in the tile. Unfortunately, there is no API to tell the Portal that your custom tile wants to hook into the events like "dashboardTileClick", "setTilesNoVisibility", "onHiddenTab". These events are managed only for DynamicTiles by DashboardLoadingManager.js.

Well, as a workaround you could handle some window/documents events by your own, like "window.blur", "window.focus" and "document.hidden".
But, that's not enough. When you have an update loop running (e.g. via setInterval() to refresh data in your custom tile, it would run the whole time. It also runs when the user has click, on a different tile, endless.

Parameterise Custom Tile via FLP Config - now it's getting dirty!!


DON'T TRY THIS AT HOME or in PRODUCTION!! ?

Imagine you want to dynamically load data in you custom tile, e.g. a specific type of notifications, within the custom tile. And you don't want to create another tile app and parameterise the OData/REST call.

Then you could add just another visualisation to the business app configuration, into the visualizations object:
"customTile2-display": {
"vizType": "project.namespace.customTile2",
"vizConfig": {
"sap.app": {
"title": "{{customTile2.title}}",
"subTitle": "{{customTile2.subtitle}}"
},
"sap.flp": {
"target": {
"inboundId": "data-display",
"parameters": {
"param1" : {
"value": {
"value": "new param value1",
"format": "plain"
}
}
}
}
}
}

The parameters property is read via a readVisualizations function readVisualizations.getOutbound(). 

During debugging the custom tile, the _oProperties object now has created the target URL with the configured param1 parameter:

targetURL"#data-display?param1=new%20param%20value1&sap-ui-app-id-hint=tutorial.app.one.app1"

Debug code line: https://github.com/citoki/portal-custom-tile/blob/0d91424d1f373307d49d605d66ed33f69fe7db86/app-custo...


Technically this is the hash/link which should be called after clicking on the custom tile.

You could now parse this string and extract the param1 parameter and use this during the initialisation for the custom tile.

Code - End-To-End Sample


See code sample for an End-To-End sample project: https://github.com/citoki/portal-custom-tile
16 Comments
Many thanks! It is something I was trying days ago, good contribution!
0 Kudos
Hi Steffen,

Nice blog. I have tried to replicate the same.However, I got below error. Could you please tell me what could be the reason



 

 
former_member183924
Active Participant
0 Kudos
Please check my sample file. Maybe the catalogs definition isn't correct.

 

https://github.com/citoki/portal-custom-tile/blob/master/flp/portal-site/CommonDataModel.json#L17-L2...
0 Kudos
Hi Steffen,

I have tried same way but my deployement is failling.

 

manifest.json
{
"_version": "1.12.0",
"sap.flp": {
"type": "tile",
"tileSize": "1x1"
},
"sap.app": {
"id": "appui5.appui5",
"type": "application",
"i18n": "i18n/i18n.properties",
"applicationVersion": {
"version": "1.0.0"
},
"title": "{{appTitle}}",
"description": "{{appDescription}}",
"tags": {
"keywords": []
},
"ach": "CA-UI2-INT-FE",
"crossNavigation": {
"inbounds": {
"products-display": {
"signature": {
"parameters": {},
"additionalParameters": "allowed"
},
"semanticObject": "products",
"action": "display"
}
}
}
},
"sap.ui": {
"technology": "UI5",
"icons": {
"icon": "",
"favIcon": "",
"phone": "",
"phone@2": "",
"tablet": "",
"tablet@2": ""
},
"deviceTypes": {
"desktop": true,
"tablet": true,
"phone": true
}
},
"sap.ui5": {
"flexEnabled": false,
"componentName": "appui5.appui5",
"rootView": {
"viewName": "appui5.appui5.view.App",
"type": "XML",
"async": true,
"id": "App"
},
"dependencies": {
"minUI5Version": "1.65.6",
"libs": {
"sap.ui.core": {},
"sap.m": {},
"sap.ui.layout": {}
}
},
"contentDensities": {
"compact": true,
"cozy": true
},
"models": {
"i18n": {
"type": "sap.ui.model.resource.ResourceModel",
"settings": {
"bundleName": "appui5.appui5.i18n.i18n"
}
}
},
"resources": {
"css": [{
"uri": "css/style.css"
}]
},
"handleValidation": false,
"routing": {
"config": {
"routerClass": "sap.m.routing.Router",
"viewType": "XML",
"async": true,
"viewPath": "appui5.appui5.view",
"controlAggregation": "pages",
"controlId": "app",
"clearControlAggregation": false
},
"routes": [{
"name": "RouteApp",
"pattern": "RouteApp",
"target": ["TargetApp"]
}],
"targets": {
"TargetApp": {
"viewType": "XML",
"transition": "slide",
"clearControlAggregation": false,
"viewId": "App",
"viewName": "App"
}
}
}
}
}

business-apps
{
"_version": "3.0.0",
"identification": {
"id": "businesscustomapp",
"entityType": "businessapp",
"i18n": "i18n/businessApps.properties"
},

"payload": {

"visualizations": {

"products-displayTocustom": {
"vizType": "appui5.appui5",

"vizConfig": {
"sap.app": {
"subTitle": "CustomApp"
},
"sap.flp": {
"target": {
"inboundId": "products-display"
}

}

}

}

}

}

}

 

CMD file.json

 
{
"_version": "3.0.0",
"identification": {
"id": "a9c311ca-997a-4d7b-ba57-fbec6c8c0002-1585300229069",
"entityType": "bundle"
},
"payload": {
"catalogs": [{
"_version": "3.0.0",
"identification": {
"id": "defaultCatalogId",
"title": "{{title}}",
"entityType": "catalog",
"i18n": "i18n/defaultCatalogId.properties"
},
"payload": {
"viz": [{
"id": "businesscustomapp",
"vizId": "products-displayTocustom"
}]
}
}],
"groups": [{
"_version": "3.0.0",
"identification": {
"id": "defaultGroupId",
"title": "{{title}}",
"entityType": "group",
"i18n": "i18n/defaultGroupId.properties"
},
"payload": {
"viz": [{
"id": "businesscustomapp-0-1573035031268",
"appId": "businesscustomapp",
"vizId": "products-displayTocustom"
}]
}
}],
"sites": [{
"_version": "3.0.0",
"identification": {
"id": "504f54af-cda5-47eb-bb3d-05072e32db70-1585300229069",
"entityType": "site",
"title": "SAP Fiori launchpad site on Cloud Foundry",
"description": "SAP Fiori launchpad site on Cloud Foundry, deployed from SAP Web IDE"
},
"payload": {
"config": {
"ushellConfig": {
"renderers": {
"fiori2": {
"componentData": {
"config": {
"applications": {
"Shell-home": {}
}
}
}
}
}
}
},
"groupsOrder": ["defaultGroupId"],
"sap.cloud.portal": {
"config": {
"theme.id": "sap_fiori_3",
"theme.active": ["sap_fiori_3", "sap_belize_hcb", "sap_belize_hcw"]
}
}
}
}]
}
}

 

undefined Error : "Failed to search all appHostIds, missing apps: businesscustomapp""


 

 
0 Kudos
Hi Bradguver Jesus Villavicencio Rojas,

 

Are you able to create the custom tile? I am getting deployement fail.Please check my code in below comment.

 

Regards

Charan
former_member183924
Active Participant
0 Kudos
I think there isn't an UI5 app 'businesscustomapp' deployed in your space/HTML5 repository. The app appui5.appui5 is only the UI5 app for the visualisation of the custom tile. The target app is also needed.
0 Kudos
Hello CHARANRAJ THARIGONDA, What I could recommend and what in my case solved my problem is that for example in your business-apps.json you put as id not businesscustomapp but something like businesscustomapp.app (keep in mind that you must update the rest and change businesscustomapp by businesscustomapp.app) and after this change create an HTML5 Module where the name of your application would be app and its namespace would be businesscustomapp, after making all these changes make your build and deploy.

Regards.
0 Kudos
Hello CHARANRAJ THARIGONDA, What I could recommend and what in my case solved my problem is that for example in your business-apps.json you put as id not businesscustomapp but something like businesscustomapp.app (keep in mind that you must update the rest and change businesscustomapp by businesscustomapp.app) and after this change create an HTML5 Module where the name of your application would be app and its namespace would be businesscustomapp, after making all these changes make your build and deploy.

Regards.
lucia_wu
Employee
Employee
0 Kudos
Hi Steffen,

It is very useful to me, I have done it like your code, but once I deploy BTP, there are always errors, I don't know what happened, do you have the same situation?

 
cdm.js:25 2021-08-13 15:10:06.197399 Cannot resolve tile 'com.cimer.gomad.tile#customTileDisplay': no visualization type found for vizTypeId 'com.cimer.gomad.customTile'
- sap/ushell/adapters/cdm/LaunchPageAdapter

 

Best regards,

Lucia
0 Kudos
Hello Steffen,

The blog is very useful, i tried implementing the custom tile to our application, but while deploying its throwing an error

"Error: Failed to import site, tenantId: 745e90f7-790b-4daf-aad6-c1a2a050d34d, instanceId: 52bb9099-d9a6-4a08-8be2-e4ccb29a0a56, appHostIds: {"appHostIds":["4e4e21d4-0869-4573-8eba-c0b932b0f1f8"],"boundAppHostIds":[],"instanceId":"52bb9099-d9a6-4a08-8be2-e4ccb29a0a56"}, xsAppNameServicesMap: {"html5-apps-repo-dt":"4e4e21d4-0869-4573-8eba-c0b932b0f1f8!b10875|html5-apps-repo-uaa!b1129","com.sap.portal.no.business.service":"irouting-sandbox-bd-internal!b10875"}, blueBoxMetadataStr: undefined Error : "The following IntentHintId defined in CommonDataModel.json do not exists - verify the existence of the following Intents: Set(1) { 'cs.customtile#customtile-manage' }"', error type 'TsInternalServerErrorException'
Process failed."

and not allowing to deploy.

Can you kindly help me in resolving the issue ?

 

Thanks and Reagrds,

Alam

 
zameer0448
Participant
0 Kudos
Hi Alam,

 

I am also facing the same issue, can you help me how you resolved this issue,

 

thanks and regards

Zameer Ahamad
gregorw
Active Contributor
Hi Steffen,

thank you for this post. I would suggest you make clear that this is talking about the Launchpad Module that can be used in MTA's and for multi tenant apps. It's not about the Launchpad Service.

Best Regards
Gregor
0 Kudos

Hi Steffen,

How to Implement Refresh Interval feature in custom tile, as shown in bellow snapshot?

former_member833341
Participant
0 Kudos
Hi, how do i test this in local?
former_member833341
Participant
0 Kudos
Hi lucia.wu , were you able to resolve this error?
ReenaWadhwa
Advisor
Advisor
0 Kudos
Hi Shivaraj,

Can you explain more about how you did it?

Thanks in Advance!!!