SAPUI5: Data Binding

Ein sehr mächtiges Feature innerhalb von WPF ist das Data Binding. Das WPF-Databinding bietet für Anwendungen eine einfache und konsistente Möglichkeit, Daten darzustellen und mit ihnen zu interagieren. Elemente können aus einer Vielzahl von Datenquellen (z.B. XML-Datei, Datenbank, OData-Service, usw.), in Form von Objekten, an die jeweiligen UI-Elemente gebunden werden. Das SAPUI5-Framework bietet nun ebenfalls Data Binding und auch hier gibt es, ähnlich den XML-Views, einige Parallelen zu WPF. In diesem Artikel werden nun die Grundlagen des Data Bindings innerhalb von SAPUI5, auf Basis eines kleinen Beispiels, gezeigt.

Datenquelle und Modell

Als Grundlage für das Data Binding dient in den allermeisten Fällen ein sogenanntes Modell (engl. model). Innerhalb des SAPUI5-Frameworks unterscheidet man zwischen client- und serverseitigen Modellen, z.B. gibt es das JSON-Modell. Das JSON-Modell ist ein clientseitiges Modell, bei dem die benötigten Daten aus einer JSON-Datei geladen werden. Das OData-Modell hingegen ist ein serverseitiges Modell und hier werden die Daten direkt aus dem OData-Service geladen. Bei den clientseitigen Modellen stehen der Anwendung nach der Instanziierung des Modellobjekts und dem Laden der Daten alle im Modell enthaltenen Daten zur Verfügung. Bei der Instanziierung eines serverseitigen Modells werden Daten hingegen nicht automatisch geladen. Die Daten müssen explizit angefordert werden. Wie genau die Anbindung an einen OData-Service funktioniert werde ich in einem separaten Artikel näher erläutern. Für das Beispiel, in diesem Artikel, nutzen wir ein einfaches JSON-Modell. In dieser Beispielanwendung sind die Daten in der Datei data.json abgelegt. Die Datei ist wie folgt aufgebaut:

Um auf die Daten innerhalb der gezeigten JSON-Datei zugreifen zu können muss ein entsprechendes Model erzeugt bzw. die Daten geladen werden werden.

Instanziierung und Laden der Daten

Um mit den Daten, aus der oben gezeigte JSON-Datei, arbeiten zu können muss diese Datei in ein entsprechendes Modell geladen werden. Das SAPUI5-Framework bietet standardmäßig die folgenden Modelle:

Für dieses Beispiel sieht die Instanziierung des Modells und das Laden der Daten wie folgt aus:

In Zeile 19 wird eine Instanz der Klasse sap.ui.model.json.JSONModel erzeugt. In Zeile 21 wird dann die Methode loadData aufgerufen, um die Daten zu laden. Diese Methode erhält als Parameter den Pfad zu der JSON-Datei. Mit dem Aufruf von sap.ui.getCore().setModel(oModel) in Zeile 24 wird das Modell innerhalb der Anwendung zur Verfügung gestellt (das ist vergleichbar mit dem Setzen des DataContext in einer WPF-Anwendung).

In Zeile 27 erfolgt dann das DataBinding. Hier wird eine Instanz des Controls sap.m.Text erzeugt und die Eigenschaft text des Controls wird an den LastName Wert des Eintrags mit der ID 0 gebunden. Über den Pfad /Contacts/0/LastName erfolgt der Zugriff auf den Wert der Eigenschaft LastName innerhalb des Modells.

Benannte Modelle

Es ist ja durchaus denkbar, dass eine Anwendung mehrere Modelle verwenden möchte. Das SAPUI5-Framework bietet daher die Möglichkeit den Modellen einen Namen zu geben. In dem oben gezeigten Beispiel wurde mit dem Aufruf von sap.ui.getCore().setModel(oModel) das Standardmodell der Anwendung ersetzt. Die Methode setModel hat jetzt noch einen weiteren Parameter namens sName. Mit diesem Parameter ist es möglich die einzelnen Modelle zu benennen. Bei einem Binding muss dann allerdings auch der Name des Modells angegebenen werden. Das sieht dann wie folgt aus:

Property Binding

Property Bindings ermöglichen es, die Eigenschaften eines Controls mit bestimmten Werten in einem Modell zu verbinden. Sobald solch eine Verbindung aufgebaut wurde, wird jede Änderung am Wert in einem Modell in der Control-Eigenschaft wiedergespiegelt. Bei der Verwendung eines bidirektionalen Bindings gilt dies auch in umgekehrter Reihenfolge. Um eine Bindung zwischen einer Control-Eigenschaft und einem Modell herzustellen benötigt man zwei Parameter:

  • den Pfad zum Wert im Modell
  • den Namen der Eigenschaft, die gebunden werden soll

Das DataBinding innerhalb eines JavaScript- bzw. XML-Views sieht dann wie folgt aus:

JavaScript-View

XML-View

bindProperty-Methode und BindingMode

Sollen die Eigenschaften eines Controls nicht bei der Instanziierung, sondern zu einem späteren Zeitpunkt im Code an einen Modellwert gebunden werden, kann die Methode bindProperty verwendet werden. Diese Methode wird von jedem SAPUI5-Control bereitgestellt. Daneben ist es, genau wie in WPF, möglich den Datenfluss, über die Eigenschaft mode vom Typ BindingMode, zu steuern. Dafür stehen die folgenden Werte zur Auswahl:

  • Default – der BindingMode des Models wird übernommen/verwendet
  • OneTime – der Wert wird nur einmal aus dem Model geladen
  • OneWay – der Wert wird wird nur aus dem Model gelesen und nicht zurückgeschrieben (model -> view)
  • TwoWay – bidirektionales Binding, d.h. der Wert wird aus dem Model gelesen und auch wieder zurückgeschrieben (model <-> view)

Die folgende Tabelle gibt eine Übersicht über den BindingMode und die Default-Einstellung in den verschiedenen Modellen:

Model
One-Way
Two-Way
One-Time
JSON-Model

(default)
XML-Model

(default)
OData V2 Model

(default)
OData V4 Model

(default)
Resource Model

JavaScript-View

XML-View

Innerhalb eines XML-Views lässt sich die bindProperty-Methode nicht aufrufen, es ist aber dennoch möglich den BindingMode zu setzen:

Aggregation Binding

Neben dem Property Binding gibt es noch einen weiteren Binding-Typ namens Aggregation Binding. Mit dieser Art des Bindings werden automatisch Kindelemente entsprechend der Modelldaten erzeugt. Das ist vor allem für die Anzeige innerhalb von ItemsControls interessant (z.B. ComboBoxen, Tabellen, Listen, usw.). Möchte man die oben gezeigten Modelldaten z.B. als Liste ausgeben könnte das wie folgt aussehen:

Das List-Element hat ein Attribut namens items und das geschachtelte Element items:

  • Das Attribut items=“{/contacts}“ bindet die Kinder, in unserem Falle die einzelnen Kontakte, unseres JSON-Models an die Liste. Das alleine reicht jedoch nicht aus um die Liste anzuzeigen. Mit diesem Binding wird lediglich der übergeordnete Pfad gesetzt.
  • Das verschachtelte items-Element dient dann als Template für die eigentliche Anzeige. In diesem Beispiel wird ein StandardListItem als Vorlage für die einzelnen Listenzeilen verwendet. Diese Vorlage wird dann für jeden Wert im Modell geklont, und der Bindungspfad des aggregierten Elements wird auf den entsprechenden Modellknoten festgelegt.

Das Binding innerhalb des StandardListItems für die Eigenschaften title und description sind relativ zu contacts. Das bedeutet, dann man nicht den kompletten Pfad title={/contacts/LastName} angeben muss, es genügt einfach title={name} zu schreiben. Der sap.m-Namespace bietet, neben dem StandardListItem, noch die folgenden Templates:

  • ActionListItem
  • DisplayListItem
  • CustomListItem
  • ObjectListItem

Innerhalb eines JavaScript-Views muss für das AggregationBinding ein zusätzliches Template definiert werden. Hier mal beispielhaft das Binding für eine ComboBox:

Dabei muss dieses Template nicht zwingend aus einem einzelnen Control bestehen. Ist es ebenso möglich ein geschachteltes Template aus verschiedenen Controls zu erstellen und dieses dann zu binden. Neben der oben gezeigten Möglichkeit kann auch die bindAggregation-Methode verwendet werden:

Einige Controls bietet direkt eine Methode für das AggregationBinding:

Verwenden einer Factory

Ein großer Vorteil beim Einsatz der bindAggregation Methode ist, dass zusätzlich eine Factory verwendet werden kann. Ein häufiger Anwendungsfall ist die dynamische Erstellung der Benutzeroberfläche, z. B. wenn zusätzliche oder andere untergeordnete Controls angezeigt werden sollen, falls bestimmte Kriterien erfüllt sind. Dieses Konzept ähnelt den WPF DataTemplates in Verbindung mit einem DataTemplateSelector.

Um zu verhindern, dass das UI zu viele Einträge rendern muss gibt es innerhalb des Modells eine Größenbeschränkung! Dieses Limit legt die Anzahl der Einträge für das Aggregation Binding fest. Das Standardlimit ist auf 100 Einträge festgelegt! Das bedeutet, dass Controls die kein Paging unterstützen oder nicht in der Lage sind Daten in Blöcken anzufordern (z.B. die sap.m.ComboBox) nur 100 Einträge anzeigen (auch wenn das Model mehr als 100 Einträge enthält). Um dieses Verhalten zu ändern stellt das Model die Methode setSizeLimit zu Verfügung (mit dieser Methode ist es möglich das Limit selbst zu definieren).

Element Binding

Konzeptionell sind sich das Aggregation Binding und das Element Binding recht ähnlich. Beide Konzepte erlauben es ein UI-Control an Modelldaten zu binden und beide Konzepte erzeugen innerhalb dieses UI-Controls einen Datenkontext, der ein relatives DataBinding für alle Kindelemente erlaubt. Beim Aggregation Binding werden dabei automatisch so viele untergeordnete UI-Elemente erzeugt, die benötigt werden, um die Modelldaten anzuzeigen. Das ElementBinding hingegen bezieht sich dabei auf eine einzelnes Element und eignet sich daher besonders gut für Master-Detail-Szenarieren.

In dem oben gezeigten Beispiel wird der Kontext auf den ersten Eintrag innerhalb der Liste gesetzt. Das ist vor allem für Container oder Layouts interessant, die viele untergordnete Controls enthalten. Damit werden diese untergeordneten Controls in die Lage versetzt Eigenschaften des gleichen Modellobjekts zu visualisieren.

Fazit

Das SAPUI5-Framework bietet einige Konzepte für das DataBidning, die es so auch im WPF-Umfeld gibt. Gerade Konzepte wie das DataBinding erleichtern die Erstellung von Business-Anwendungen ungemein. Das DataBinding bietet eine einfache und konsistente Möglichkeit, Daten darzustellen und mit ihnen zu interagieren. Elemente können aus einer Vielzahl von Datenquellen (z.B. XML-Datei, Datenbank, OData-Service, usw.), in Form von Objekten, an die jeweiligen UI-Elemente gebunden werden. In einem weiteren Artikel werde ich, auf Basis einer MVC-Anwendung, auf weitere Möglichkeiten des DataBindings eingehen.

Fork me on GitHub