Fiori Launchpad Custom Tiles: Eigene Kacheln mit Live-Daten erstellen

kategorie
Fiori
Veröffentlicht
autor
Johannes

Fiori Launchpad Tiles sind der Einstiegspunkt fuer deine Anwendungen. Neben einfachen statischen Kacheln kannst du Dynamic Tiles mit Live-Daten erstellen, die Kennzahlen direkt auf dem Launchpad anzeigen. Dieser Artikel zeigt alle Tile-Typen und ihre Implementierung in ABAP Cloud.

Tile-Typen im Ueberblick

Das Fiori Launchpad unterstuetzt verschiedene Kachel-Typen:

Tile-TypBeschreibungDatenquelle
Static TileFester Titel und IconKeine
Dynamic TileLive-Kennzahl mit TrendOData Service
News TileNews-Feed mit BildernOData Service
Custom TileFreie UI5-ImplementierungBeliebig
KPI TileSmart Business KPIKPI Framework

Static Tiles

Static Tiles sind die einfachste Form - ein Icon, Titel und optionaler Untertitel.

Wann verwenden?

  • Einfache App-Navigation ohne Live-Daten
  • Konfigurationsanwendungen
  • Dokumentation und Hilfe-Links

Target Mapping Konfiguration

Die Tile-Konfiguration erfolgt ueber das Target Mapping im Fiori Launchpad Designer oder ueber den SAP Fiori Launchpad Content Manager.

Semantic Object: Travel
Action: manage
Tile:
- Title: Reisen verwalten
- Subtitle: Travel Management
- Icon: sap-icon://flight
- Info: Transaktional

Inbound Navigation in der App

In der UI5-App (manifest.json) definierst du den Inbound:

{
"sap.app": {
"id": "ztravel.manage",
"crossNavigation": {
"inbounds": {
"Travel-manage": {
"semanticObject": "Travel",
"action": "manage",
"title": "{{appTitle}}",
"subTitle": "{{appSubTitle}}",
"icon": "sap-icon://flight",
"signature": {
"parameters": {},
"additionalParameters": "allowed"
}
}
}
}
}
}

Dynamic Tiles - Live-Daten anzeigen

Dynamic Tiles zeigen aktuelle Kennzahlen direkt auf dem Launchpad. Die Daten werden ueber einen OData-Service geladen.

Architektur

┌─────────────────────────────────────────────────────────────────────┐
│ Fiori Launchpad │
│ │
│ ┌──────────────────┐ ┌──────────────────┐ ┌──────────────────┐ │
│ │ Dynamic Tile │ │ Dynamic Tile │ │ Static Tile │ │
│ │ │ │ │ │ │ │
│ │ 42 │ │ € 125k │ │ ┌────────┐ │ │
│ │ Offene Tasks │ │ Umsatz Q1 │ │ │ Icon │ │ │
│ │ ↑ +5 │ │ ↓ -3% │ │ └────────┘ │ │
│ └────────┬─────────┘ └────────┬─────────┘ └──────────────────┘ │
│ │ │ │
│ ▼ ▼ │
│ OData Request OData Request │
│ │ │ │
└───────────┼─────────────────────┼────────────────────────────────────┘
│ │
▼ ▼
┌─────────────┐ ┌─────────────┐
│ CDS View │ │ CDS View │
│ ZI_TaskKPI │ │ ZI_SalesKPI │
└─────────────┘ └─────────────┘

CDS View fuer Dynamic Tile

Die Basis ist ein CDS View mit einer einzigen Entity, die die Tile-Daten liefert:

@EndUserText.label: 'Open Tasks KPI for Tile'
@ObjectModel.representativeKey: 'TileId'
@ObjectModel.semanticKey: ['TileId']
define view entity ZI_OpenTasksKPI
as select from ztasks
{
-- Dummy-Schluessel (nur ein Datensatz)
key 1 as TileId,
-- Anzahl offene Tasks
@EndUserText.label: 'Offene Tasks'
cast( count(*) as abap.int4 ) as Number,
-- Einheit (optional)
@EndUserText.label: 'Einheit'
cast( 'Tasks' as abap.char(10) ) as NumberUnit,
-- Status-Info
@EndUserText.label: 'Info'
cast( 'Aktuell' as abap.char(20) ) as Info,
-- Trend: Vergleich zu gestern
@EndUserText.label: 'Trend'
case
when count(*) > (
select count(*) from ztasks as t2
where t2.created_at < @( cl_abap_context_info=>get_system_date( ) )
and t2.status = 'O'
)
then cast( 'Up' as abap.char(10) )
else cast( 'Down' as abap.char(10) )
end as NumberTrend,
-- Trend-Pfeil (1=up, 0=none, -1=down)
@EndUserText.label: 'State Arrow'
case
when count(*) > (
select count(*) from ztasks as t2
where t2.created_at < @( cl_abap_context_info=>get_system_date( ) )
and t2.status = 'O'
)
then 1
else -1
end as StateArrow
}
where
status = 'O' -- Nur offene Tasks
group by
status

Vereinfachte Version mit Custom Entity

Fuer komplexere Berechnungen verwende eine Custom Entity:

@EndUserText.label: 'Sales KPI for Dynamic Tile'
@ObjectModel.query.implementedBy: 'ABAP:ZCL_SALES_KPI_QUERY'
define custom entity ZC_SalesKPI
{
key TileId : abap.int4;
Number : abap.dec(15,2);
NumberUnit : abap.char(10);
NumberFactor : abap.char(10);
NumberDigits : abap.int4;
NumberState : abap.char(20);
StateArrow : abap.int1;
Info : abap.char(30);
InfoState : abap.char(20);
TargetNumber : abap.dec(15,2);
Subtitle : abap.char(50);
}

Query-Implementierung

CLASS zcl_sales_kpi_query DEFINITION
PUBLIC FINAL
CREATE PUBLIC.
PUBLIC SECTION.
INTERFACES if_rap_query_provider.
ENDCLASS.
CLASS zcl_sales_kpi_query IMPLEMENTATION.
METHOD if_rap_query_provider~select.
" Umsatz berechnen
SELECT SUM( amount ) AS total_sales
FROM zsales
WHERE fiscal_year = @( substring( cl_abap_context_info=>get_system_date( ), 1, 4 ) )
INTO @DATA(lv_current_sales).
" Vorjahresumsatz
SELECT SUM( amount ) AS total_sales
FROM zsales
WHERE fiscal_year = @( substring( cl_abap_context_info=>get_system_date( ), 1, 4 ) - 1 )
INTO @DATA(lv_last_year_sales).
" Trend berechnen
DATA(lv_trend) = COND i(
WHEN lv_current_sales > lv_last_year_sales THEN 1
WHEN lv_current_sales < lv_last_year_sales THEN -1
ELSE 0 ).
" Prozentuale Aenderung
DATA(lv_change_pct) = COND decfloat34(
WHEN lv_last_year_sales > 0
THEN ( lv_current_sales - lv_last_year_sales ) / lv_last_year_sales * 100
ELSE 0 ).
" Status ermitteln
DATA(lv_state) = COND string(
WHEN lv_change_pct >= 5 THEN 'Good'
WHEN lv_change_pct >= 0 THEN 'Neutral'
ELSE 'Critical' ).
" Ergebnis aufbereiten
DATA(lt_result) = VALUE ztt_sales_kpi(
( TileId = 1
Number = lv_current_sales / 1000 " In Tausend
NumberUnit = 'EUR'
NumberFactor = 'k'
NumberDigits = 1
NumberState = lv_state
StateArrow = lv_trend
Info = |{ lv_change_pct DECIMALS = 1 }% vs. Vorjahr|
InfoState = lv_state
TargetNumber = lv_last_year_sales * '1.05'
Subtitle = |Stand: { cl_abap_context_info=>get_system_date( ) DATE = USER }| )
).
io_response->set_data( lt_result ).
io_response->set_total_number_of_records( 1 ).
ENDMETHOD.
ENDCLASS.

Service Binding fuer Dynamic Tile

@EndUserText.label: 'KPI Tiles Service'
define service ZSB_KPI_TILES {
expose ZI_OpenTasksKPI;
expose ZC_SalesKPI;
}

Target Mapping mit dynamischer Kachel

Im Launchpad Content Manager oder Designer:

Semantic Object: Task
Action: manage
Tile Configuration:
- Tile Type: Dynamic
- Title: Offene Tasks
- Subtitle: Task Management
- Icon: sap-icon://task
- Service URL: /sap/opu/odata4/sap/zsb_kpi_tiles/srvd/sap/zsb_kpi_tiles/0001/
- Entity Set: ZI_OpenTasksKPI
- Number: Number
- Number Unit: NumberUnit
- Number State: NumberState
- State Arrow: StateArrow
- Info: Info

Dynamic Tile Felder

FeldBeschreibungBeispiel
NumberHauptkennzahl42
NumberUnitEinheit”EUR”, “Tasks”
NumberFactorSkalierung”k”, “M”, “B”
NumberDigitsDezimalstellen0, 1, 2
NumberStateFarbe”Good”, “Critical”, “Neutral”
StateArrowTrendpfeil1=up, 0=none, -1=down
InfoZusatzinfo”+5% vs. Vorjahr”
InfoStateInfo-Farbe”Good”, “Critical”

News Tiles

News Tiles zeigen einen Feed mit Bildern und Beschreibungen.

CDS View fuer News Tile

@EndUserText.label: 'Company News for Tile'
@ObjectModel.semanticKey: ['NewsId']
define view entity ZI_CompanyNews
as select from znews
{
key news_id as NewsId,
@EndUserText.label: 'Titel'
title as Title,
@EndUserText.label: 'Beschreibung'
description as Description,
@EndUserText.label: 'Bild-URL'
image_url as ImageUrl,
@EndUserText.label: 'Link'
link_url as LinkUrl,
@EndUserText.label: 'Datum'
published_at as PublishedAt
}
where
published_at <= $session.system_date
order by
published_at descending

News Feed Konfiguration

{
"tileType": "sap.ushell.ui.tile.DynamicTile",
"tileConfiguration": {
"semantic_object": "News",
"semantic_action": "display",
"service_url": "/sap/opu/odata4/sap/zsb_news/srvd/sap/zsb_news/0001/",
"service_refresh_interval": 300,
"collection": "ZI_CompanyNews",
"title": "{Title}",
"subtitle": "{Description}",
"imageUrl": "{ImageUrl}",
"targetURL": "{LinkUrl}"
}
}

Target Mapping - Navigation konfigurieren

Das Target Mapping verbindet die Kachel mit der Zielanwendung.

Konzept

┌─────────────────────────────────────────────────────────────────────┐
│ Target Mapping │
├─────────────────────────────────────────────────────────────────────┤
│ │
│ Semantic Object: "SalesOrder" │
│ Action: "manage" │
│ │
│ ┌─────────────────────────────────────────────────────────────┐ │
│ │ Tile (Was der User sieht) │ │
│ │ - Title: "Auftraege verwalten" │ │
│ │ - Icon: sap-icon://sales-order │ │
│ │ - Dynamic Data: OData Service │ │
│ └─────────────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────────────────────────────┐ │
│ │ Target (Wohin navigiert wird) │ │
│ │ - Application Type: SAPUI5 Fiori App │ │
│ │ - URL: /sap/bc/ui5_ui5/sap/zsalesorder_app │ │
│ │ - Parameters: SalesOrg=1000 │ │
│ └─────────────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────┘

Launchpad Content Manager

In ABAP Cloud erfolgt die Konfiguration ueber den SAP Fiori Launchpad Content Manager:

  1. Apps anlegen (Service Binding verknuepfen)
  2. Catalogs erstellen (Apps gruppieren)
  3. Groups definieren (Sichtbare Gruppen im Launchpad)
  4. Roles zuweisen (Wer sieht was)

Programmatische Konfiguration

Fuer automatisiertes Deployment kannst du die Konfiguration als JSON exportieren:

{
"apps": [
{
"id": "ztravel_manage",
"vizId": "Travel-manage",
"vizType": "sap.ushell.StaticAppLauncher",
"title": "Reisen verwalten",
"subTitle": "Travel Management",
"icon": "sap-icon://flight",
"info": "Travel",
"target": {
"type": "URL",
"url": "/sap/bc/ui5_ui5/sap/ztravel_manage/index.html"
}
}
],
"catalogs": [
{
"id": "ZTRAVEL_CATALOG",
"title": "Travel Management",
"apps": ["ztravel_manage", "ztravel_approve"]
}
],
"groups": [
{
"id": "ZTRAVEL_GROUP",
"title": "Reisemanagement",
"apps": ["ztravel_manage"]
}
]
}

Dynamic Tile mit Live-Daten - Vollstaendiges Beispiel

Schritt 1: Datenbank-Tabelle

@EndUserText.label: 'Purchase Orders'
@AbapCatalog.enhancement.category: #NOT_EXTENSIBLE
define table zpurchase_orders {
key client : abap.clnt;
key po_id : abap.numc(10);
vendor_id : abap.numc(10);
amount : abap.curr(15,2);
currency : abap.cuky;
status : abap.char(1); -- O=Open, A=Approved, R=Rejected
created_at : timestampl;
created_by : abap.uname;
}

Schritt 2: KPI CDS View

@EndUserText.label: 'Open PO KPI for Dynamic Tile'
@ObjectModel.query.implementedBy: 'ABAP:ZCL_OPEN_PO_KPI'
define custom entity ZC_OpenPurchaseOrdersKPI
{
key TileId : abap.int4;
Number : abap.int4;
NumberUnit : abap.char(10);
NumberState : abap.char(20);
StateArrow : abap.int1;
Info : abap.char(50);
InfoState : abap.char(20);
Subtitle : abap.char(50);
}

Schritt 3: Query-Implementierung

CLASS zcl_open_po_kpi DEFINITION
PUBLIC FINAL
CREATE PUBLIC.
PUBLIC SECTION.
INTERFACES if_rap_query_provider.
ENDCLASS.
CLASS zcl_open_po_kpi IMPLEMENTATION.
METHOD if_rap_query_provider~select.
DATA: lv_today TYPE d,
lv_yesterday TYPE d.
lv_today = cl_abap_context_info=>get_system_date( ).
lv_yesterday = lv_today - 1.
" Offene POs heute
SELECT COUNT(*) FROM zpurchase_orders
WHERE status = 'O'
INTO @DATA(lv_open_today).
" Offene POs gestern (Snapshot)
SELECT COUNT(*) FROM zpurchase_orders
WHERE status = 'O'
AND created_at < @( CONV timestampl( lv_today ) )
INTO @DATA(lv_open_yesterday).
" Differenz
DATA(lv_diff) = lv_open_today - lv_open_yesterday.
" Trend bestimmen
DATA(lv_arrow) = COND i(
WHEN lv_diff > 0 THEN 1 " Mehr offene POs = schlecht
WHEN lv_diff < 0 THEN -1 " Weniger = gut
ELSE 0 ).
" Status (umgekehrte Logik: weniger offen = gut)
DATA(lv_state) = COND string(
WHEN lv_diff <= 0 THEN 'Good'
WHEN lv_diff <= 5 THEN 'Neutral'
ELSE 'Critical' ).
" Info-Text
DATA(lv_info) = COND string(
WHEN lv_diff > 0 THEN |+{ lv_diff } seit gestern|
WHEN lv_diff < 0 THEN |{ lv_diff } seit gestern|
ELSE |Unveraendert| ).
" Ergebnis
DATA(lt_result) = VALUE ztt_open_po_kpi(
( TileId = 1
Number = lv_open_today
NumberUnit = 'POs'
NumberState = lv_state
StateArrow = lv_arrow
Info = lv_info
InfoState = lv_state
Subtitle = |Offene Bestellungen| )
).
io_response->set_data( lt_result ).
io_response->set_total_number_of_records( 1 ).
ENDMETHOD.
ENDCLASS.

Schritt 4: Service Definition

@EndUserText.label: 'Purchase Order KPI Service'
define service ZSB_PO_KPI {
expose ZC_OpenPurchaseOrdersKPI;
}

Schritt 5: Service Binding erstellen

In ADT:

  1. Rechtsklick auf Service Definition → New Service Binding
  2. Binding Type: OData V4 - UI
  3. Aktivieren und URL testen

Schritt 6: Launchpad Konfiguration

Im Launchpad Content Manager die Kachel konfigurieren:

ParameterWert
Tile TypeDynamic
Service URL/sap/opu/odata4/sap/zsb_po_kpi/srvd/sap/zsb_po_kpi/0001/
Entity SetZC_OpenPurchaseOrdersKPI
Number PropertyNumber
Number UnitNumberUnit
Number StateNumberState
State ArrowStateArrow
InfoInfo
Refresh Interval30 (Sekunden)

Refresh-Intervall und Performance

Empfehlungen

AnwendungsfallRefresh-Intervall
Echtzeit-Kennzahlen10-30 Sekunden
Standard-KPIs60-300 Sekunden
Tageswerte900+ Sekunden
Statische DatenManuell

Performance-Optimierung

METHOD if_rap_query_provider~select.
" Caching fuer aufwaendige Berechnungen
DATA: lv_cache_key TYPE string,
ls_cached TYPE zs_kpi_cache.
lv_cache_key = |PO_KPI_{ sy-datum }|.
" Cache pruefen (z.B. Shared Memory oder Application Buffer)
IF cache_is_valid( lv_cache_key ).
ls_cached = get_from_cache( lv_cache_key ).
ELSE.
" Berechnung durchfuehren
ls_cached = calculate_kpi( ).
put_to_cache( lv_cache_key, ls_cached ).
ENDIF.
io_response->set_data( VALUE #( ( ls_cached ) ) ).
ENDMETHOD.

IAM und Berechtigungen

Die Sichtbarkeit von Tiles wird ueber IAM Business Catalogs gesteuert:

IAM Business Catalog: ZPO_BC_BUYER
├── IAM App: ZPO_MANAGE (Bestellungen verwalten)
├── IAM App: ZPO_KPI (KPI Tile Service)
└── Restriction Type: ZPO_RT (Org-Einheiten)

Ohne Zuweisung des Business Catalogs zur Business Role ist die Kachel nicht sichtbar.

Catalog fuer KPI-Services

<?xml version="1.0" encoding="utf-8"?>
<iam:catalog
xmlns:iam="http://www.sap.com/iam"
catalogId="ZPO_BC_KPI">
<catalogDescription>Purchase Order KPI Tiles</catalogDescription>
<catalogApps>
<iamApp>ZPO_KPI_TILES</iamApp>
</catalogApps>
</iam:catalog>

Best Practices

1. Semantische Objekte konsistent benennen

Semantic Object: PurchaseOrder
Actions:
- manage → Hauptanwendung
- display → Nur Anzeige
- create → Schnellerfassung
- approve → Genehmigungsapp
- kpi → Dashboard

2. Tile-Typen richtig waehlen

SzenarioTile-Typ
App ohne Live-DatenStatic
Einzelne Kennzahl mit TrendDynamic
News-FeedNews
Komplexe VisualisierungCustom (UI5)

3. Performance beachten

  • KPI-Abfragen unter 200ms halten
  • Aggregationen auf DB-Ebene durchfuehren
  • Caching fuer aufwaendige Berechnungen

4. Konsistente Farbgebung

NumberStateBedeutungFarbe
GoodPositivGruen
NeutralNormalGrau
CriticalAchtungRot
ErrorFehlerRot (dunkel)

Haeufige Fehler

ProblemUrsacheLoesung
Tile leerService nicht erreichbarService Binding aktivieren
”No data”Falsches Entity SetEntity Set im Target Mapping pruefen
Kein RefreshIntervall = 0Refresh-Intervall setzen
Tile nicht sichtbarKeine BerechtigungBusiness Catalog zuweisen
Falsche KennzahlProperty-Mapping falschFeldnamen in Konfiguration pruefen

Verwandte Themen


Zusammenfassung

Fiori Launchpad Tiles bieten verschiedene Moeglichkeiten zur App-Navigation:

AspektStatic TileDynamic Tile
Live-DatenNeinJa
OData-ServiceNicht noetigErforderlich
KomplexitaetGeringMittel
Use CaseEinfache NavigationKPIs, Kennzahlen

Kern-Implementierung:

  1. CDS View oder Custom Entity fuer KPI-Daten
  2. Service Definition und Service Binding (OData V4)
  3. Target Mapping im Launchpad Content Manager
  4. IAM Business Catalog fuer Berechtigungen

Mit Dynamic Tiles zeigst du relevante Kennzahlen direkt auf dem Launchpad und ermoeglichst Nutzern einen schnellen Ueberblick ueber wichtige Geschaeftsdaten.