Die Frage Fiori Elements oder Freestyle Fiori? beschäftigt viele UI-Entwickler im SAP-Umfeld. Beide Ansätze haben ihre Berechtigung - doch wann ist welcher der richtige? Dieser Artikel liefert eine fundierte Entscheidungshilfe mit konkreten Code-Beispielen und einer klaren Entscheidungsmatrix.
Überblick: Die zwei Welten der Fiori-Entwicklung
┌────────────────────────────────────────────────────────────────────┐│ SAP Fiori UI-Entwicklung │├────────────────────────────────────────────────────────────────────┤│ ││ ┌─────────────────────────┐ ┌─────────────────────────────┐ ││ │ Fiori Elements │ │ Freestyle Fiori │ ││ │ (Annotations-basiert) │ │ (SAPUI5 Custom) │ ││ ├─────────────────────────┤ ├─────────────────────────────┤ ││ │ • Automatische UI │ │ • Volle UI-Kontrolle │ ││ │ • CDS Annotations │ │ • XML Views + Controller │ ││ │ • Kein JavaScript │ │ • JavaScript/TypeScript │ ││ │ • Templates (List, OP) │ │ • Custom Components │ ││ │ • SAP Updates inklusive │ │ • Eigene Wartung │ ││ └─────────────────────────┘ └─────────────────────────────┘ ││ ││ Aufwand: ●○○○○ Aufwand: ●●●●● ││ Flexibilität: ●●○○○ Flexibilität: ●●●●● ││ │└────────────────────────────────────────────────────────────────────┘Kurzvergleich
| Aspekt | Fiori Elements | Freestyle Fiori |
|---|---|---|
| Entwicklungsaufwand | Gering (1-2 Tage) | Hoch (1-2 Wochen) |
| UI-Flexibilität | Begrenzt (Templates) | Unbegrenzt |
| Wartungsaufwand | Minimal | Kontinuierlich |
| Lernkurve | CDS Annotations | SAPUI5/JavaScript |
| UX-Konsistenz | Automatisch SAP-konform | Manuell sicherzustellen |
| Updates | Von SAP mitgeliefert | Eigenständig |
| Debugging | Schwieriger (Framework) | Einfacher (eigener Code) |
Was sind Fiori Elements?
Fiori Elements ist SAPs Ansatz für deklarative UI-Entwicklung. Statt UI-Code zu schreiben, definierst du die Oberfläche durch Annotations im CDS-Datenmodell.
So funktioniert Fiori Elements
┌─────────────────────────────────────────────────────────────────┐│ Fiori Elements │├─────────────────────────────────────────────────────────────────┤│ ││ CDS View + Annotations ││ │ ││ ▼ ││ ┌───────────────────┐ ││ │ Fiori Elements │ ──▶ Automatische UI-Generierung ││ │ Runtime │ ││ └───────────────────┘ ││ │ ││ ▼ ││ ┌───────────────────────────────────────┐ ││ │ Fertige Fiori App │ ││ │ • List Report │ ││ │ • Object Page │ ││ │ • Filter, Actions, Navigation │ ││ └───────────────────────────────────────┘ ││ │└─────────────────────────────────────────────────────────────────┘Verfügbare Fiori Elements Templates
| Template | Beschreibung | Typische Anwendung |
|---|---|---|
| List Report | Tabelle mit Filtern und Actions | Stammdaten-Übersichten |
| Object Page | Detail-Ansicht mit Facets | Einzelsatz-Bearbeitung |
| Analytical List Page | Kombination aus Chart und Tabelle | Reporting, Dashboards |
| Worklist | Task-orientierte Liste | Genehmigungen, Aufgaben |
| Overview Page | Kachel-basiertes Dashboard | Management-Übersichten |
| Form Object Page | Formular-fokussierte Ansicht | Datenerfassung |
Fiori Elements Code-Beispiel
CDS Projection View mit UI Annotations:
@EndUserText.label: 'Bestellung - Projection'@Metadata.allowExtensions: truedefine root view entity ZC_ORDER provider contract transactional_query as projection on ZI_ORDER{ key OrderId, CustomerId, OrderDate, TotalAmount, Currency, Status,
/* Associations */ _Customer, _Items}Metadata Extension (separate Datei .ddlx):
@Metadata.layer: #COREannotate view ZC_ORDER with{ /* Header-Informationen */ @UI.headerInfo: { typeName: 'Bestellung', typeNamePlural: 'Bestellungen', title: { value: 'OrderId' }, description: { value: '_Customer.CustomerName' } }
/* Facets für Object Page */ @UI.facet: [ { id: 'OrderHeader', purpose: #STANDARD, type: #IDENTIFICATION_REFERENCE, label: 'Bestelldaten', position: 10 }, { id: 'OrderItems', purpose: #STANDARD, type: #LINEITEM_REFERENCE, label: 'Positionen', position: 20, targetElement: '_Items' } ]
/* Feld-Annotationen */ @UI.lineItem: [{ position: 10, importance: #HIGH }] @UI.identification: [{ position: 10 }] @UI.selectionField: [{ position: 10 }] OrderId;
@UI.lineItem: [{ position: 20, importance: #HIGH }] @UI.identification: [{ position: 20 }] @UI.selectionField: [{ position: 20 }] CustomerId;
@UI.lineItem: [{ position: 30 }] @UI.identification: [{ position: 30 }] OrderDate;
@UI.lineItem: [{ position: 40, criticality: 'StatusCriticality' }] @UI.identification: [{ position: 40 }] Status;
@UI.lineItem: [{ position: 50 }] @UI.identification: [{ position: 50 }] @Semantics.amount.currencyCode: 'Currency' TotalAmount;}Ergebnis: Eine vollständige List Report + Object Page Anwendung ohne eine einzige Zeile JavaScript!
Was ist Freestyle Fiori?
Freestyle Fiori bedeutet die manuelle Entwicklung mit dem SAPUI5-Framework. Du hast volle Kontrolle über jeden Aspekt der Benutzeroberfläche.
So funktioniert Freestyle Fiori
┌─────────────────────────────────────────────────────────────────┐│ Freestyle Fiori │├─────────────────────────────────────────────────────────────────┤│ ││ ┌────────────────┐ ┌────────────────┐ ┌────────────────┐ ││ │ XML Views │ │ Controllers │ │ Models │ ││ │ (Layout) │ │ (JavaScript) │ │ (Daten) │ ││ └───────┬────────┘ └───────┬────────┘ └───────┬────────┘ ││ │ │ │ ││ └────────────────────┼────────────────────┘ ││ │ ││ ▼ ││ ┌────────────────────────────────────────────────────────┐ ││ │ Custom Fiori Application │ ││ │ • Eigene Komponenten │ ││ │ • Custom Layouts │ ││ │ • Volle JavaScript-Kontrolle │ ││ └────────────────────────────────────────────────────────┘ ││ │└─────────────────────────────────────────────────────────────────┘Freestyle Fiori Code-Beispiel
manifest.json (Auszug):
{ "sap.app": { "id": "com.mycompany.orderapp", "type": "application", "title": "Bestellungen verwalten", "dataSources": { "mainService": { "uri": "/sap/opu/odata4/sap/zapi_order/srvd_a2x/sap/zui_order/0001/", "type": "OData", "settings": { "odataVersion": "4.0" } } } }}View (XML):
<mvc:View controllerName="com.mycompany.orderapp.controller.OrderList" xmlns="sap.m" xmlns:mvc="sap.ui.core.mvc" xmlns:f="sap.f" xmlns:core="sap.ui.core">
<f:DynamicPage id="orderPage"> <f:title> <f:DynamicPageTitle> <f:heading> <Title text="Bestellungen"/> </f:heading> <f:actions> <Button text="Neue Bestellung" type="Emphasized" press=".onCreateOrder"/> <Button text="Exportieren" icon="sap-icon://excel-attachment" press=".onExport"/> </f:actions> </f:DynamicPageTitle> </f:title>
<f:header> <f:DynamicPageHeader> <f:content> <FlexBox wrap="Wrap"> <Input id="searchField" placeholder="Bestellung suchen..." liveChange=".onSearch" width="300px"/> <Select id="statusFilter" change=".onStatusFilterChange" items="{/StatusOptions}"> <core:Item key="{Key}" text="{Text}"/> </Select> <DateRangeSelection id="dateFilter" change=".onDateFilterChange"/> </FlexBox> </f:content> </f:DynamicPageHeader> </f:header>
<f:content> <Table id="orderTable" items="{/Orders}" growing="true" growingThreshold="50" mode="MultiSelect" selectionChange=".onSelectionChange">
<headerToolbar> <OverflowToolbar> <ToolbarSpacer/> <Button text="Löschen" icon="sap-icon://delete" enabled="{= ${/SelectedCount} > 0}" press=".onDeleteSelected"/> </OverflowToolbar> </headerToolbar>
<columns> <Column width="10em"> <Text text="Bestellnummer"/> </Column> <Column width="15em"> <Text text="Kunde"/> </Column> <Column width="10em"> <Text text="Datum"/> </Column> <Column width="8em" hAlign="End"> <Text text="Betrag"/> </Column> <Column width="10em"> <Text text="Status"/> </Column> </columns>
<items> <ColumnListItem type="Navigation" press=".onOrderPress"> <cells> <ObjectIdentifier title="{OrderId}" text="{OrderType}"/> <Text text="{CustomerName}"/> <Text text="{ path: 'OrderDate', type: 'sap.ui.model.type.Date', formatOptions: { style: 'medium' } }"/> <ObjectNumber number="{ path: 'TotalAmount', type: 'sap.ui.model.type.Currency', formatOptions: { showMeasure: false } }" unit="{Currency}"/> <ObjectStatus text="{StatusText}" state="{= ${Status} === 'COMPLETE' ? 'Success' : ${Status} === 'CANCELED' ? 'Error' : 'Warning' }"/> </cells> </ColumnListItem> </items> </Table> </f:content> </f:DynamicPage></mvc:View>Controller (JavaScript):
sap.ui.define([ "sap/ui/core/mvc/Controller", "sap/ui/model/json/JSONModel", "sap/ui/model/Filter", "sap/ui/model/FilterOperator", "sap/m/MessageBox", "sap/ui/export/Spreadsheet"], function(Controller, JSONModel, Filter, FilterOperator, MessageBox, Spreadsheet) { "use strict";
return Controller.extend("com.mycompany.orderapp.controller.OrderList", {
onInit: function() { // Lokales View-Model für UI-State var oViewModel = new JSONModel({ SelectedCount: 0, StatusOptions: [ { Key: "", Text: "Alle Status" }, { Key: "NEW", Text: "Neu" }, { Key: "INPROCESS", Text: "In Bearbeitung" }, { Key: "COMPLETE", Text: "Abgeschlossen" }, { Key: "CANCELED", Text: "Storniert" } ] }); this.getView().setModel(oViewModel); },
onSearch: function(oEvent) { var sQuery = oEvent.getParameter("newValue"); var oTable = this.byId("orderTable"); var oBinding = oTable.getBinding("items");
var aFilters = []; if (sQuery) { aFilters.push(new Filter({ filters: [ new Filter("OrderId", FilterOperator.Contains, sQuery), new Filter("CustomerName", FilterOperator.Contains, sQuery) ], and: false })); }
// Kombiniere mit Status-Filter this._applyFilters(aFilters); },
onStatusFilterChange: function(oEvent) { var sStatus = oEvent.getParameter("selectedItem").getKey(); this._sCurrentStatus = sStatus; this._applyFilters(); },
onDateFilterChange: function(oEvent) { var oDateRange = oEvent.getSource(); this._oDateFrom = oDateRange.getDateValue(); this._oDateTo = oDateRange.getSecondDateValue(); this._applyFilters(); },
_applyFilters: function(aAdditionalFilters) { var aFilters = aAdditionalFilters || [];
// Status-Filter if (this._sCurrentStatus) { aFilters.push(new Filter("Status", FilterOperator.EQ, this._sCurrentStatus)); }
// Datum-Filter if (this._oDateFrom && this._oDateTo) { aFilters.push(new Filter("OrderDate", FilterOperator.BT, this._oDateFrom, this._oDateTo)); }
var oTable = this.byId("orderTable"); var oBinding = oTable.getBinding("items"); oBinding.filter(aFilters.length > 0 ? new Filter({ filters: aFilters, and: true }) : []); },
onSelectionChange: function(oEvent) { var iSelectedCount = oEvent.getSource().getSelectedItems().length; this.getView().getModel().setProperty("/SelectedCount", iSelectedCount); },
onCreateOrder: function() { // Navigation zu Create-View var oRouter = this.getOwnerComponent().getRouter(); oRouter.navTo("orderCreate"); },
onOrderPress: function(oEvent) { // Navigation zu Detail-View var oItem = oEvent.getSource(); var sOrderId = oItem.getBindingContext().getProperty("OrderId");
var oRouter = this.getOwnerComponent().getRouter(); oRouter.navTo("orderDetail", { orderId: sOrderId }); },
onDeleteSelected: function() { var oTable = this.byId("orderTable"); var aSelectedItems = oTable.getSelectedItems();
MessageBox.confirm( "Möchten Sie " + aSelectedItems.length + " Bestellung(en) wirklich löschen?", { title: "Löschen bestätigen", onClose: function(sAction) { if (sAction === MessageBox.Action.OK) { this._deleteOrders(aSelectedItems); } }.bind(this) } ); },
_deleteOrders: function(aItems) { // OData Delete-Aufrufe var oModel = this.getView().getModel("mainService");
aItems.forEach(function(oItem) { var sPath = oItem.getBindingContext().getPath(); oModel.remove(sPath, { success: function() { // Erfolgreich gelöscht }, error: function(oError) { MessageBox.error("Fehler beim Löschen: " + oError.message); } }); }); },
onExport: function() { var oTable = this.byId("orderTable"); var oBinding = oTable.getBinding("items");
var oSpreadsheet = new Spreadsheet({ workbook: { columns: [ { label: "Bestellnummer", property: "OrderId" }, { label: "Kunde", property: "CustomerName" }, { label: "Datum", property: "OrderDate", type: "Date" }, { label: "Betrag", property: "TotalAmount", type: "Number" }, { label: "Währung", property: "Currency" }, { label: "Status", property: "StatusText" } ] }, dataSource: oBinding, fileName: "Bestellungen.xlsx" });
oSpreadsheet.build(); } });});Vergleich: Deutlich mehr Code, aber auch deutlich mehr Kontrolle über jeden Aspekt der UI!
Entscheidungsmatrix: Wann welcher Ansatz?
Fiori Elements empfohlen
| Kriterium | Begründung |
|---|---|
| ✅ Standard CRUD-Operationen | List Report + Object Page perfekt geeignet |
| ✅ Schnelle Entwicklung gefordert | Faktor 5-10x schneller als Freestyle |
| ✅ SAP-Standard UX gewünscht | Automatisch Fiori Design Guidelines |
| ✅ Wenig UI-Expertise im Team | Keine JavaScript-Kenntnisse nötig |
| ✅ Langfristige Wartbarkeit wichtig | SAP-Updates automatisch integriert |
| ✅ Transaktionale Apps (RAP) | Perfekte Integration mit RAP |
| ✅ Analytical Apps mit Standard-Charts | Analytical List Page nutzen |
Freestyle Fiori empfohlen
| Kriterium | Begründung |
|---|---|
| ✅ Hochgradig individuelle UI | Keine Template-Grenzen |
| ✅ Custom Visualisierungen | Eigene Charts, Diagramme, Karten |
| ✅ Komplexe Interaktionsmuster | Drag & Drop, Wizards, Multi-Step |
| ✅ Third-Party Libraries nötig | Integration externer Komponenten |
| ✅ Pixel-perfektes Design gefordert | Volle Layout-Kontrolle |
| ✅ Gaming/Gamification-Elemente | Animationen, Spielmechaniken |
| ✅ Offline-First Anforderung | Vollständige Offline-Logik implementierbar |
Entscheidungsbaum
Start: Neue Fiori App erforderlich │ ▼ ┌──────────────────────┐ │ Ist die UI-Anforderung │ │ mit Templates abbildbar?│ └─────────┬────────────┘ │ ┌────────┴────────┐ │ │ ▼ ▼ JA NEIN │ │ ▼ ▼┌───────────────┐ ┌────────────────┐│ Braucht die │ │ → FREESTYLE ││ App komplexe │ │ FIORI ││ Custom-Logik? │ └────────────────┘└──────┬────────┘ │ ┌────┴────┐ │ │ ▼ ▼ JA NEIN │ │ ▼ ▼┌─────────────────┐ ┌────────────────────┐│ Kann die Logik │ │ → FIORI ELEMENTS ││ in ABAP (RAP) │ │ (Rein deklarativ)││ implementiert │ └────────────────────┘│ werden? │└────────┬────────┘ │ ┌────┴────┐ │ │ ▼ ▼ JA NEIN │ │ ▼ ▼┌──────────────────┐ ┌────────────────────┐│ → FIORI ELEMENTS │ │ → FREESTYLE FIORI ││ mit RAP Backend│ │ oder Hybrid │└──────────────────┘ └────────────────────┘Hybrid-Ansatz: Fiori Elements + Custom Extensions
In vielen Fällen ist ein Hybrid-Ansatz der beste Kompromiss: Fiori Elements als Basis mit gezielten Custom Extensions.
Extension-Möglichkeiten in Fiori Elements
| Extension-Typ | Beschreibung | Aufwand |
|---|---|---|
| Header Extension | Custom Header Content | Niedrig |
| Footer Extension | Custom Actions im Footer | Niedrig |
| Custom Column | Eigene Spalte mit Custom Renderer | Mittel |
| Custom Section | Eigene Section in Object Page | Mittel |
| Controller Extension | Override von Controller-Methoden | Hoch |
| Building Block | Wiederverwendbare UI-Komponenten | Hoch |
Beispiel: Custom Section Extension
manifest.json (Auszug):
{ "sap.ui5": { "extends": { "extensions": { "sap.ui.controllerExtensions": { "sap.fe.templates.ObjectPage.ObjectPageController": { "controllerName": "com.mycompany.orderapp.ext.ObjectPageExt" } } } }, "routing": { "targets": { "OrderObjectPage": { "options": { "settings": { "content": { "body": { "sections": { "CustomSection": { "template": "com.mycompany.orderapp.ext.CustomSection", "title": "Lieferverfolgung", "position": { "anchor": "OrderItems", "placement": "After" } } } } } } } } } } }}Custom Section Fragment:
<core:FragmentDefinition xmlns="sap.m" xmlns:core="sap.ui.core" xmlns:macros="sap.fe.macros">
<VBox class="sapUiSmallMargin"> <Title text="Sendungsverfolgung"/>
<!-- Custom Map Component --> <FlexBox height="300px" width="100%"> <core:HTML content="{= ${DeliveryMapHtml} }"/> </FlexBox>
<ProgressIndicator percentValue="{= ${DeliveryProgress} }" displayValue="{= ${DeliveryStatus} }" state="{= ${DeliveryProgress} === 100 ? 'Success' : 'Information' }" showValue="true"/>
<HBox class="sapUiTinyMarginTop"> <Button text="Tracking-Details" type="Transparent" icon="sap-icon://map" press=".extension.com.mycompany.orderapp.ext.ObjectPageExt.onShowTrackingDetails"/> </HBox> </VBox></core:FragmentDefinition>Controller Extension:
sap.ui.define([ "sap/ui/core/mvc/ControllerExtension", "sap/m/MessageBox"], function(ControllerExtension, MessageBox) { "use strict";
return ControllerExtension.extend("com.mycompany.orderapp.ext.ObjectPageExt", {
// Override: Wird beim Laden der Object Page aufgerufen override: { onPageReady: function(mParameters) { // Custom Initialisierung this._loadDeliveryTracking(); } },
// Custom Method für Tracking-Details onShowTrackingDetails: function(oEvent) { var oContext = this.base.getView().getBindingContext(); var sTrackingNumber = oContext.getProperty("TrackingNumber");
// Custom Dialog öffnen if (!this._oTrackingDialog) { this._oTrackingDialog = sap.ui.xmlfragment( "com.mycompany.orderapp.ext.TrackingDialog", this ); this.base.getView().addDependent(this._oTrackingDialog); }
this._oTrackingDialog.open(); },
_loadDeliveryTracking: function() { // Custom API-Aufruf für Tracking-Daten var oContext = this.base.getView().getBindingContext(); if (!oContext) return;
var sOrderId = oContext.getProperty("OrderId");
// Beispiel: Externe Tracking-API fetch("/api/tracking/" + sOrderId) .then(function(response) { return response.json(); }) .then(function(data) { // Update Model mit Tracking-Daten var oModel = this.base.getView().getModel(); oModel.setProperty(oContext.getPath() + "/DeliveryProgress", data.progress); oModel.setProperty(oContext.getPath() + "/DeliveryStatus", data.status); }.bind(this)); } });});Vor- und Nachteile im Detail
Fiori Elements: Vorteile
-
Entwicklungsgeschwindigkeit
- Bis zu 80% weniger Code
- Keine JavaScript-Entwicklung
- Standard-Patterns sofort verfügbar
-
Wartbarkeit
- SAP liefert Updates automatisch
- Accessibility-Konformität garantiert
- Performance-Optimierungen inklusive
-
Konsistenz
- Automatisch Fiori Design Guidelines
- Einheitliche UX über alle Apps
- Mobile-ready ohne Zusatzaufwand
-
Integration mit RAP
- Nahtlose Anbindung an Backend
- Draft Handling out-of-the-box
- Validations und Actions automatisch verfügbar
Fiori Elements: Nachteile
-
Eingeschränkte Flexibilität
- Nur vordefinierte Templates
- Custom Layouts schwer umsetzbar
- Komplexe Interaktionen nicht möglich
-
Debugging-Herausforderungen
- Framework-generierter Code schwer zu debuggen
- Annotation-Fehler schwer zu finden
- Weniger direkte Kontrolle
-
Lernkurve bei Annotations
- Viele Annotations zu lernen
- Dokumentation manchmal lückenhaft
- Trial-and-Error bei komplexen Szenarien
Freestyle Fiori: Vorteile
-
Volle Kontrolle
- Jedes UI-Element individuell gestaltbar
- Komplexe Interaktionen möglich
- Pixel-perfekte Designs umsetzbar
-
Third-Party Integration
- Externe Libraries integrierbar (Charts, Maps, etc.)
- Custom Components wiederverwendbar
- Keine Framework-Grenzen
-
Debugging
- Eigener Code einfacher zu debuggen
- Volle Browser-DevTools-Unterstützung
- Klare Fehlerquellen
Freestyle Fiori: Nachteile
-
Hoher Entwicklungsaufwand
- 5-10x mehr Code als Fiori Elements
- JavaScript/TypeScript-Expertise nötig
- Längere Entwicklungszeiten
-
Wartungsaufwand
- Updates manuell einpflegen
- Accessibility selbst sicherstellen
- Performance-Optimierung selbst verantworten
-
Konsistenz-Risiko
- UX-Inkonsistenzen möglich
- Fiori Guidelines manuell einhalten
- Team-Koordination wichtiger
Code-Vergleich: Dieselbe Funktionalität
Anforderung: Bestellliste mit Filter und Navigation
Fiori Elements (ca. 50 Zeilen Annotations):
@Metadata.layer: #COREannotate view ZC_ORDER with{ @UI.headerInfo: { typeName: 'Bestellung', typeNamePlural: 'Bestellungen', title: { value: 'OrderId' } }
@UI.lineItem: [{ position: 10 }] @UI.selectionField: [{ position: 10 }] OrderId;
@UI.lineItem: [{ position: 20 }] @UI.selectionField: [{ position: 20 }] CustomerId;
@UI.lineItem: [{ position: 30 }] OrderDate;
@UI.lineItem: [{ position: 40, criticality: 'StatusCriticality' }] @UI.selectionField: [{ position: 30 }] Status;}Freestyle Fiori (ca. 250 Zeilen Code):
- manifest.json: 50 Zeilen
- View.xml: 80 Zeilen
- Controller.js: 120 Zeilen
Fazit: Für Standard-CRUD-Szenarien ist Fiori Elements deutlich effizienter.
Praxisbeispiele: Wann welchen Ansatz?
Beispiel 1: Stammdatenpflege (→ Fiori Elements)
Anforderung: CRUD für Kundenstammdaten mit Adresse und Ansprechpartnern
Warum Fiori Elements:
- Standard-CRUD mit List Report + Object Page
- Child-Entities (Adressen, Ansprechpartner) als Facets
- Validations über RAP
- Draft Handling für lange Formulare
Beispiel 2: Dashboard mit Custom Charts (→ Freestyle)
Anforderung: Management-Dashboard mit interaktiven Visualisierungen
Warum Freestyle:
- Custom D3.js oder Highcharts-Visualisierungen
- Drag & Drop für Widget-Anordnung
- Real-time Updates via WebSocket
- Komplexe Filter-Interaktionen
Beispiel 3: Genehmigungsworkflow (→ Fiori Elements)
Anforderung: Genehmigungsprozess für Bestellanforderungen
Warum Fiori Elements:
- Worklist Template ideal für Aufgabenlisten
- Actions (Genehmigen, Ablehnen) einfach zu definieren
- Standard-Navigation zwischen Aufgaben
- Mobile-ready ohne Zusatzaufwand
Beispiel 4: Produktkonfigurator (→ Freestyle)
Anforderung: Interaktiver 3D-Produktkonfigurator
Warum Freestyle:
- 3D-Rendering mit Three.js
- Komplexe Interaktionen (Rotation, Zoom)
- Dynamische Preisberechnung im Frontend
- Custom Validierungslogik
Beispiel 5: Hybrid - Bestellverwaltung mit Tracking
Anforderung: Standard-Bestellverwaltung + Lieferverfolgung mit Karte
Lösung: Fiori Elements + Custom Extension
- List Report und Object Page als Basis
- Custom Section mit Map-Integration
- Controller Extension für Tracking-API
Migration zwischen den Ansätzen
Von Freestyle zu Fiori Elements
In manchen Fällen kann eine Migration von Freestyle zu Fiori Elements sinnvoll sein:
| Schritt | Beschreibung |
|---|---|
| 1. Analyse | Welche Features sind Standard vs. Custom? |
| 2. RAP-Backend | Backend auf RAP umstellen |
| 3. Annotations | UI-Annotations für Standard-Features erstellen |
| 4. Custom Extensions | Nicht-Standard-Features als Extensions implementieren |
| 5. Migration | Schrittweise umstellen, Template für Template |
Von Fiori Elements zu Freestyle
Selten nötig, aber möglich wenn Anforderungen stark wachsen:
| Schritt | Beschreibung |
|---|---|
| 1. XML-Export | Generierte XML-Views als Basis exportieren |
| 2. Controller | Controller-Logik aus Extensions übernehmen |
| 3. Refactoring | Framework-Abhängigkeiten entfernen |
| 4. Custom Model | OData-Anbindung anpassen |
Best Practices
Für Fiori Elements
| DO | DON’T |
|---|---|
| ✅ Annotations in separater .ddlx Datei | ❌ Zu viele Annotations inline in CDS |
| ✅ RAP für komplexe Logik nutzen | ❌ Logik in UI-Extensions packen |
| ✅ Standard-Templates verwenden | ❌ Exzessive Anpassungen vornehmen |
| ✅ Metadata Extensions für Lesbarkeit | ❌ Alles in eine Datei schreiben |
| ✅ Building Blocks für Wiederverwendung | ❌ Jede Extension einzeln implementieren |
Für Freestyle Fiori
| DO | DON’T |
|---|---|
| ✅ Fiori Design Guidelines folgen | ❌ Komplett eigenes Design erfinden |
| ✅ sap.ui.core Controls verwenden | ❌ Native HTML ohne guten Grund |
| ✅ Routing für Navigation nutzen | ❌ Eigene Navigation bauen |
| ✅ Models für Datenbindung | ❌ DOM-Manipulation direkt |
| ✅ Controller-Logik klein halten | ❌ Alles in einen Controller packen |
Fazit: Der richtige Ansatz für dein Projekt
Wähle Fiori Elements wenn:
- Du Standard-CRUD-Anwendungen baust
- Du schnell produktiv sein musst
- Du wenig Frontend-Expertise hast
- Du langfristige Wartbarkeit priorisierst
Wähle Freestyle Fiori wenn:
- Du hochgradig individuelle UIs brauchst
- Du Custom Visualisierungen oder Third-Party Libraries integrierst
- Du komplexe Interaktionsmuster implementierst
- Du volle Kontrolle über jeden Aspekt benötigst
Wähle Hybrid wenn:
- 80% Standard, 20% Custom
- Spezielle Sections oder Columns nötig sind
- Du die Vorteile beider Welten kombinieren willst
In der Praxis zeigt sich: 80-90% der transaktionalen SAP-Anwendungen können effektiv mit Fiori Elements umgesetzt werden. Der Hybrid-Ansatz deckt weitere 5-8% ab. Nur für wirklich spezielle Anforderungen ist reines Freestyle Fiori der richtige Weg.
Siehe auch:
- SAP Fiori Elements: UI ohne Code erstellen
- RAP Tutorial Teil 1: Erste Fiori App
- RAP Basics: RESTful ABAP Programming
- CDS Annotations im Detail