SAP MDK: Gezielte Entitätssuche mit clientAPI.read und Action Binding

Einführung: Von der Liste zur gezielten Suche in SAP MDK
In vielen SAP MDK-Anwendungen ist der Standard-Flow klar: Ein komplettes EntitySet, wie eine Produktliste, wird in einer Tabelle oder Liste angezeigt. Der Nutzer wählt einen Eintrag aus, und eine Navigation Action leitet ihn auf eine Detailseite weiter, wobei der Kontext des ausgewählten Eintrags automatisch übergeben wird.
Doch was ist, wenn dieser Umweg über eine Liste nicht gewünscht ist? Was, wenn der Anwender eine bekannte ProductID
direkt eingeben und sofort zur Detailseite des spezifischen Produkts springen möchte?
Genau dieses Szenario lösen wir in diesem Tutorial. Ich zeige Ihnen, wie Sie mit der MDK-Schnittstelle clientAPI.read
und dem mächtigen Action Binding eine effiziente, gezielte Suche nach einer einzelnen Entität realisieren.
Tipp: Falls Sie neu im Thema MDK sind, empfehlen wir für das grundlegende Setup das offizielle SAP Tutorial: Getting Started with the SAP Mobile Development Kit
Anwendungsfall: Direktsprung zur Produktdetailseite
Wir entwerfen eine einfache Benutzeroberfläche, die bewusst auf eine vollflächige Liste verzichtet. Stattdessen bieten wir dem Nutzer:
- Ein Eingabefeld, um eine
ProductID
einzugeben. - Einen "Suchen"-Button, der eine MDK-Rule auslöst.
- Diese Rule führt einen gezielten Backend-Aufruf mit einem Filter aus (z.B.
Products?$filter=ProductID eq 'HT-1000'
). - Das Ergebnis – idealerweise genau ein Datensatz – wird per Action Binding an die Detailseite übergeben.
- Die Detailseite zeigt die Informationen des gefundenen Produkts an.
Dieser Ansatz ist deutlich performanter und benutzerfreundlicher für Anwendungsfälle, in denen die Kennung des gesuchten Objekts bereits bekannt ist (z.B. beim Scannen eines Barcodes).
Der Kern: Die Rule mit clientAPI.read
und Action Binding
Das Herzstück unserer Logik ist eine JavaScript-Rule. Diese wird ausgeführt, sobald der Nutzer auf den "Suchen"-Button klickt. Sie liest den Wert aus dem Eingabefeld, führt den read
-Aufruf aus und navigiert im Erfolgsfall zur nächsten Seite.
Hier ist der kommentierte Code der Rule Search_Product_Rule.js
:
/**
* Liest eine Produkt-ID aus der UI, ruft das Backend mit einem Filter auf
* und bindet das Ergebnis an die nächste Action.
* @param {{ evaluateTargetPath: Function, executeAction: Function, read: Function, getPageProxy: Function }} clientAPI
*/
export default function Search_Product_Rule(clientAPI) {
// 1. Wert aus dem Eingabefeld mit der ID 'ProductIdInput' auslesen
const productId = clientAPI.evaluateTargetPath('#Control:ProductIdInput/#Value');
// 2. Eingabe validieren: Ist das Feld leer?
if (!productId || productId.length === 0) {
// Informative Nachricht für den Benutzer anzeigen
return clientAPI.executeAction({
"Name": "/demosampleapp/Actions/GenericToastMessage.action",
"Properties": {
"Message": "Bitte geben Sie eine Produkt-ID ein.",
"Duration": 4
}
});
}
// 3. Gezielter OData-Read-Aufruf
// Service-Pfad, Entitätsmenge, leeres Array für alle Properties, und die Filter-Query
return clientAPI.read(
"/demosampleapp/Services/YourService.service", // Pfad zu Ihrem Service
"Products", // Name des EntitySets
[], // Leeres Array, um alle Properties zu laden
`$filter=ProductID eq ${productId}` // Die OData-Filter-Query
).then((data) => {
const pageProxy = clientAPI.getPageProxy();
// 4. Prüfen, ob Daten zurückgegeben wurden
if (data && data.length > 0) {
// WICHTIG: Das erste gefundene Element als Kontext für die nächste Action setzen
pageProxy.setActionBinding(data.getItem(0));
// Navigations-Action zur Detailseite ausführen
return clientAPI.executeAction("/demosampleapp/Actions/Navigation_Product_Result.action");
} else {
// Kein Produkt gefunden, Benutzer informieren
return clientAPI.executeAction({
"Name": "/demosampleapp/Actions/GenericToastMessage.action",
"Properties": {
"Message": `Kein Produkt für die ID '${productId}' gefunden.`,
"Duration": 5
}
});
}
}).catch(error => {
console.error("Fehler beim Lesen der Produktdaten: ", error);
// Fehlerbehandlung für den Fall, dass der Backend-Call fehlschlägt
return clientAPI.executeAction({
"Name": "/demosampleapp/Actions/GenericToastMessage.action",
"Properties": {
"Message": "Fehler beim Abrufen der Produktdaten.",
"Duration": 5
}
});
});
}
Die Implementierung im Detail: Seiten und Controls
Seite 1: Die Suchmaske (ProductSearch.page
)
Der Aufbau dieser Seite ist minimal und fokussiert sich auf die Eingabe:
FormCell.SimpleProperty
: Ein Eingabefeld zur Aufnahme der Produkt-ID.- Wichtig: Vergeben Sie unter der Eigenschaft
_Name
eine eindeutige ID, zum BeispielProductIdInput
. Nur so können Sie mitclientAPI.evaluateTargetPath('#Control:ProductIdInput/#Value')
sicher darauf zugreifen.
- Wichtig: Vergeben Sie unter der Eigenschaft
Button
: Ein einfacher Button, der die Suche auslöst.OnPress
-Ereignis: Binden Sie hier als auszuführende Aktion Ihre MDK-Rule (/demosampleapp/Rules/Search_Product_Rule.js
).
Seite 2: Die Detailanzeige (ProductDetail.page
)
Diese Seite dient der reinen Darstellung der Produktdetails. Sie benötigt kein explizites Target
auf das EntitySet in ihren Metadaten, da der Kontext dynamisch durch die setActionBinding()
-Methode aus der Rule gesetzt wird.
Verwenden Sie UI-Controls wie
ObjectHeader
,FormCell.Title
,FormCell.SimpleProperty
, etc., um die Daten ansprechend darzustellen.Binden Sie die
Value
-Eigenschaft (oder eine entsprechende Eigenschaft des Controls) direkt an die Properties des Produkts. Das Binding erfolgt automatisch auf den Kontext, densetActionBinding
bereitgestellt hat.- Beispiel für den Produktnamen:
{Name}
- Beispiel für den Preis:
{Price}
- Beispiel für die Beschreibung:
{ShortDescription}
- Beispiel für den Produktnamen:
Dank setActionBinding
steht der Datenkontext des einzelnen, gelesenen Produkts auf der Zielseite zur Verfügung – genau so, als hätten Sie den Eintrag aus einer Liste ausgewählt.
Wichtiger Hinweis: Falls die Navigation Action zur
ProductDetail.page
eine Validation Rule enthält, kann dadurch das ActionBinding gestört werden und es werden keine Daten angezeigt. Am besten validierst du die Eingaben ausschließlich in derSearch_Product_Rule.js
Fazit: Volle Kontrolle und bessere Performance
Die Kombination aus clientAPI.read
und pageProxy.setActionBinding()
ist ein extrem mächtiges Werkzeug im SAP MDK. Sie ermöglicht es Ihnen, von Standard-Listenmustern abzuweichen und hochspezifische, performante und benutzerfreundliche Abläufe zu erstellen.
Ihre Vorteile:
- Gezielte Datenabrufe: Sie laden nur die Daten, die wirklich benötigt werden, was die Netzwerklast und die Verarbeitungszeit reduziert.
- Schlanke UI: Vermeiden Sie unnötige Zwischenschritte über Listenansichten und bieten Sie einen direkteren Weg zum Ziel.
- Hohe Flexibilität: Sie haben die volle programmatische Kontrolle über die Daten, die Sie lesen und an die nächste Seite übergeben.
Dieser Ansatz ist ideal für Suchfunktionen, Barcode-Scanning-Integrationen oder jeden Prozess, bei dem eine Entität über ihre eindeutige ID direkt angesprungen werden soll.
FAQ: Häufige Fragen
F: Was genau macht setActionBinding()
?
A: setActionBinding(object)
setzt den Binding-Kontext für die unmittelbar danach ausgeführte Action. Wenn diese Action eine Navigation zu einer neuen Seite ist, wird das übergebene object
(in unserem Fall der gefundene Produktdatensatz) zum Binding-Kontext dieser neuen Seite. Dies erspart die manuelle Übergabe von Parametern über die Action-Properties.
F: Kann ich mit clientAPI.read
auch mehrere Filterkriterien übergeben?
A: Ja, Sie können die OData-Filter-Query beliebig komplex gestalten. Verbinden Sie mehrere Kriterien einfach mit and
oder or
. Beispiel: $filter=SupplierName eq 'SAP' and Category eq 'Software'
.
F: Ist dieser Ansatz auch für Offline-Anwendungen geeignet?
A: Ja, absolut. clientAPI.read
funktioniert nahtlos mit dem Offline-OData-Store. Die Methode liest die Daten automatisch aus der lokalen SQLite-Datenbank, sofern die Anwendung offline ist und die entsprechenden Entitäten zuvor erfolgreich synchronisiert wurden.
F: Was passiert, wenn der Filter mehr als einen Datensatz zurückgibt?
A: Der Code data.getItem(0)
sorgt dafür, dass immer nur der erste Datensatz aus dem Ergebnisarray verwendet wird. In einem Szenario, in dem ein Filter potenziell mehrere Ergebnisse liefern kann (z.B. bei einer Suche nach Namen), sollten Sie die Logik anpassen – entweder um dem Benutzer eine Auswahl zu präsentieren oder um sicherzustellen, dass die Filterlogik immer eindeutig ist.