Ahoi Matrosen! Es ist schon eine Weile her, da habe ich beschrieben, wie du mit einem OData Model Daten in deine Anwendung bekommst.
Das ist aber bei weitem noch nicht alles, wozu das OData Model in der Lage ist. Deshalb möchte ich heute daran anknüpfen. In diesem Beitrag zeige ich dir, wie du mit Hilfe deines OData Models neue Daten erzeugen kannst.
Mehr Einleitung gibt’s jetzt auch nicht. Los geht’s!
Neuer Eintrag mit createEntry
Das OData Model stellt die Methode createEntry zur Verfügung, mit der es möglich ist, einen neuen Eintrag zu erzeugen. Je nachdem, welche Entität erstellt werden soll, wird diese als Parameter an die Methode übergeben.
Eine Beispiel-Implementierung sieht zum Beispiel so aus:
onAddEntry: function() { var oModel = this.getModel(); var oContext = oModel.createEntry("/Debitor"); }
Hier wird ein neuer Eintrag vom Typ Debitor erstellt. Die Methode liefert, wie du oben siehst, einen BindingContext zurück.
Wenn du dir den neuen Eintrag anschauen möchten, dann kannst du das ganz einfach mit oContext.getObject() tun. Wie du sehen wirst ist das Objekt vom Typ Debitor, bei dem alle Eigenschaften mit dem Wert undefined vorbelegt sind. Auf Norddeutsch also: “Noch ist da nix drin!”
Der BindingContext ist an dieser Stelle ganz hilfreich, da du ihn im Anschluss nehmen und direkt auf einen View (oder was dir sonst so beliebt) binden kannst.
this.getView().setBindingContext(oContext);
Der BindingContext besteht aus dem Model, mit dem wir die createEntry-Methode aufgerufen haben, und einem generierten Path, der ungefähr so aussieht: /Debitor(‘id-XXXXXXXXXX-XX’).
Die id des Path wird im Frontend wie folgt berechnet:
return "id-" + new Date().valueOf() + "-" + iIdCounter++;
Wie wir sehen können ist da ein Date/Timestamp verarbeitet. Dadurch besteht nicht die Gefahr, dass wir doppelte IDs erzeugen.
So, an dieser Stelle halten wir kurz inne. Was ist bis jetzt passiert?
Konkret hat dein Frontend einen neuen Eintrag generiert. Dieser Eintrag ist noch nicht persistiert, bedeutet, er hängt noch allein in deiner Browser-Session.
Aber wie? Kommt deine Datenstruktur nicht aus dem Backend? Ganz recht, die Struktur kommt aus dem Backend. Der Eintrag konnte erstellt werden, da du Zugriff auf das metadata.xml deines Models hast. Das wird ganz am Anfang beim Laden deiner Anwendung vom Frontend aus dem Backend angefordert. (oder genauer: An der Stelle, wo das OData Model generiert wird).
Durch das metadata.xml weiß das Frontend, wie ein Eintrag vom Typ Debitor auszusehen hat und ist so in der Lage, einen entsprechenden Eintrag zu erstellen.
Ein neuer Eintrag wird wieder gelöscht
Wie stellst du es an, wenn du deinen gerade erstellten Eintrag wieder löschen möchtest? An dieser Stelle befindet sich dein neuer Eintrag weiterhin nur in deiner Browser-Session. Das bedeutet, dass du es nur aus deinem OData Model rauslöschen musst, ein Call ans Backend ist dafür nicht notwendig. Abhilfe schafft hier die Methode resetChanges.
Eine sehr standardisierte Implementierung sieht so aus:
onAddEntryCancel: function() { var oModel = this.getModel(); if(oModel.hasPendingChanges()) { oModel.resetChanges(); } //TODO: Do something (e.g. navigate) }
Mit der Methode hasPendingChanges fragst du ab, ob du irgendeine Änderung in deinem Model zwischengespeichert hast. Die Antwort ist entweder true oder false. Sofern du den Wert true zurück bekommst löscht du alle Änderungen in deinem Model. Beim Rückgabewert false musst du nichts machen.
Anschließend steht es dir frei, noch irgendwas zu tun. In der Regel wird auf eine Übersichtsseite zurücknavigiert. Hier bist du aber völlig frei, was du tust.
Hinweis: Solltest du deine App ins Fiori Launchpad integrieren solltest du bedenken, dass die Anwendungen sich auch durch die Header-Leiste des Launchpads navigieren lsst (z.B. der Home-Button). Das kann dazu führen, dass ggf. Änderungen oder neue Einträge im Model nicht wie erwartend gelöscht oder gespeichert werden. Diese verbleiben dann temporär im Model und sind da auch weiterhin, wenn du die Anwendung ohne Aktualisierung wieder aus dem Launchpad aufrufst. Ich habe hier schon interessante Effekte erlebt.
Ende vom Lied ist, dass ich jetzt zumeist auch beim Betreten einer Seite, also onRouteMatched ein oModel.resetChanges() mache.
Ein neuer Eintrag wird gespeichert
Beim Erstellen eines neuen Eintrags gibt es weitere Funktionen, die in der Praxis relevant sind. Natürlich musst du deinen Beitrag jetzt auch speichern können. Hier passiert jetzt nichts weiteres, als dass wir unserem Backend sagen: “Guck mal, liebes Backend, neuer Eintrag, bitte Speichern!”. Dafür gibt es die Methode submitChanges.
Die Speichermethode zum Persistieren deines Eintrags sieht in etwa so aus:
onAddEntrySubmit: function() { var oModel = this.getModel(); if(oModel.hasPendingChanges()) { oModel.submitChanges({ success: function(oData) { //TODO: Do something } }); } }
Am Anfang kommt wieder die Frage, ob du Änderungen in deinem Model hast. (Falls nicht musst du nichts tun) Der Unterschied zum Abbrechen ist unter anderem der, dass submitChanges einen asynchronen Call auslöst. Deshalb brauchst du an dieser Stelle ein Callback-Handling. Ein Hinweis genau hier: Wenn das Model im batchMode läuft, landen auch fehlerhafte Calls in der success-Methode. Hier musst du ggf. ein entsprechendes Fehler-Handling implementieren. Dies ist auch der Grund warum ich oben in dem Beispiel die error-Methode weggelassen habe. Sie wird in diesem Fall nämlich nie erreicht.
Hier ist natürlich Voraussetzungen, dass eine entsprechende Speicher-Methode im Backend implementiert ist. Ansonsten kannst du dir deine liebe Mühe nämlich sparen.
Direkt nach dem Speichern wird der gerade angelegt Eintrag neu geladen. Das ist ganz wichtig, falls sich im Backend Eigenschaften geändert haben bzw. noch hinzugekommen sind. Beispiele dafür sind z.B. eine generierte ID, Zeitstempel für letzte Änderungen oder ähnliches.
So, im Grunde war es das jetzt auch schon. Du hast einen neuen Eintrag angelegt.
Herzlichen Glückwunsch!
Debuggen mit getPendingChanges
Meistens läuft es leider nicht ganz so geschmeidig, wie das bis zu dieser Stelle klingt. Daten-Handling ist schwierig, komplex und fehleranfällig. Deshalb möchte ich dir noch die Funktion getPendingChanges zeigen. Diese ist ähnlich wie hasPendingChanges, sagt dir aber darüber hinaus noch, welche Änderungen du genau hast.
Zu meist wird diese Funktion beim Debuggen direkt im Browser genutzt und findet eher weniger Einzug in den Quellcode. Dort ist sie aber umso hilfreicher.
Mach kein create deep – Auch nicht beim Anlegen
Ganz am Ende noch ein kleiner Appell von mir: Wenn man sich ein bisschen mit SAPUI5 beschäftigt hat könnte man mitunter in die Versuchung kommen, eine Entität deep Speichern zu wollen. Gleiches gilt auch fürs Anlegen. Genau bedeutet das, dass nicht nur die Entität an sich gespeichert wird, sondern auch alle anhängenden Associations auf einmal. (das sind die Dinger, die wir bekommen, wenn wir beim Laden expands mitangeben)
Macht es nicht! Wirklich, bitte, macht es nicht!
Es ist zwar technisch möglich, ist aber vom Framework weder gewollt noch besonders gut umzusetzen.
Folgende Punkte sprechen dagegen:
- Unübersichtliche, überladene und manuelle Implementierungen im Frontend
- Unübersichtliche und überladene Implementierungen im Backend
- Erhöhter Implementierungsaufwand im Backend
- Wartungsaufwand auf beiden Seiten wird deutlich erhöht
- Steigendes Frustrationslevel
Die Alternative ist Stück für Stück jede Entität einzeln anzulegen. Anschließend wird die ganze Entität samt ihren expands geladen. Das ist eine deutlich, und ich wiederhole, deutlich bessere Umsetzung als jedes create deep.
Wenn du dir und den Entwicklern, die nach dir kommen, viel Zeit und Nerven sparen möchtest, speicherst du nicht deep!
Fazit
So schwer ist es nicht, Daten anzulegen. Zumindest wenn man es nur aus Sicht des Frontends betrachtet. Auch hier können Sachen schief gehen, aber die lassen sich alle dann doch recht schnell lösen. Ich hoffe, diese kleine Einführung hilft dir bei deinen Versuchen Daten anzulegen.
Einmal weitergedacht: Was das Ganze meist schwierig macht ist die Tatsache, dass du immer auch das Backend mitberücksichtigen musst. Fehler, die dort vorhanden sind fallen meist auch erst auf, wenn du sie aus dem Frontend heraus benutzen möchtest.
Fair ist das nicht aber das Schicksal eines Frontend-Entwicklers.
Das war’s aber erst mal wieder. Bleibt nur noch zu sagen: “Kieck mol wedder in!”
Du interessierst dich für Fiori und hast Lust coole Projekte mit uns zu machen? Wir suchen dich! Schau doch mal in unserer Stellenbeschreibung vorbei.