SAPUI5 Custom Controls – Teil 2

SAPUI5 Custom Controls – Teil 2

Der Weg zum eigenen Control

Da wir nun im letzten Teil alles über den generellen Aufbau eines SAPUI5 Custom Controls gelernt haben, möchten wir das ganze in der Praxis anwenden.

 

Die Aufgabe

Unsere Aufgabenstellung ist folgende:

Wir möchten einen Weg finden, um eine Liste von Personen gemäß hypothetischen Firmen-Design-Guidelines darzustellen. Leider reicht das UI5 Framework für die angenommene Einschränkung nicht aus.

Daher benötigen wir ein Custom Control. Genauer gesagt, benötigen wir folgende Komponenten:

  • PersonList – Darstellung unserer Liste
  • PersonListItem – Eine einzelne Zeile 
  • style.css – Custom Styling

Optional können noch folgende Files angelegt werden:

  • PersonListRenderer – Zum ausgelagerten Rendern der Liste
  • PersonListItemRenderer – Zum ausgelagterten Rendern eines Items

PersonListItem

Wir arbeiten uns von unten nach oben. D.h. wir fangen mit unserem aggregierten Control an, welches eben das einzelne Listitem wäre.

Ausgeben möchten wir Vorname, Nachname und das Alter der Person. Um noch ein Custom-CSS reinzubekommen, werden wir Personen mit einem Alter geringer als 18 anders formattieren.

Erstellen wir zunächst ein neues UI5 Projekt und legen die Files für die Custom-Controls an.

Anschließend definieren wir ein Template für das PersonListItem:

Wir wissen, dass wir mit sap.ui.define ein neues Modul anlegen, das zur Laufzeit dynamisch von unserem UI5 Framework geladen wird. Und genau so definieren wir eben auch ein Custom Control. Dieses inkludiert das sap.ui.core.Control, welches wir per .extend erweitern und zurückliefern.

Unsere PersonListItem soll 3 verschiedene Personaleigenschaften ausgeben. Daher brauchen wir 3 Properties (Vorname, Nachname, Alter).

Aggregations soll unser PersonListItem nicht haben, denn wir möchten das PersonListItem selbst als zu aggregierendes Control verwenden.

Properties anlegen

Wir benötigen 3 Properties:

  • firstName: string
  • lastName: string
  • age: int

Auf die Properties kann per get<PropertyName>() zugegriffen werden.

 

Events anlegen

Wir möchten, dass bei einem Click auf das PersonListItem ein Event ausgeführt wird. Dieses liefert eine Hallo-Message zurück.

  • sayHello
    • parameter: message(string)

Anschließend definieren wir eine Eventhandler für das HTML-onclick-Event. Falls dieses auftritt, triggern wir das sayHello-Event und liefern eine Message bestehend aus Name und „says hello“ per message-Parameter zurück.

 

CSS Styling

Wir möchten unsere 3 Properties per HTML-ul-Element anzeigen. Da ist jedoch das Problem, dass die Liste standartmäßig untereinander ihre Items anordnet. Um das zu umgehen, sagen wir per CSS-Klasssenselektor, das alle li-Elemente unter der Klasse die CSS-Property display mit dem Wert inline bekommen.

Und zusätzlich definieren wir noch ein padding von 10 Pixel und die Schriftfarbe black;

 

Renderer

Der Renderer ist das Herzstück unseres Controls. Hier wird definiert, was wann und wie in das DOM eingtragen werden soll.

Die wichtigsten Renderer-Funktionen sind dem letzten Blog-Teil zu entnehmen.

Wir möchten unser Control als div-Element und einer ul ausgeben. Zusätzlich möchten wir noch das Alter dementsprechend farblich hervorheben, basierend auf dem Kriterium, dass das Alter > 18 ist.

1: div ausgeben

Zu aller erst erstellen wir per openStart ein öffnendes div-Tag. Dieses div-Tag wird per writeControlData die Control-Referenz geschrieben und anschließend fügen wir noch unsere CSS-Klasse hinzu. Dann wird das öffnende div-Tag mit > geschlossen.

2: Liste aufbauen

Wir erstellen eine neue unordered-list. Als erstes List-Item geben wir den Vornamen der Person aus und dann den Nachnamen.

3: Alter ausgeben

Je nach dem ob die Person ein Alter > 18 hat, erstellen wir ein List-Item mit dem Style colorred oder color: green.

4: Alles schließen

Zu letzt schließen wir die Liste und abschließend das div-Tag.

 

Somit ist unser PersonListItem komplett. Im nächsten Schritt werden wir nun die PersonList definieren und unser PersonListItem aggregieren.

PersonList

 Die PersonList soll einen Titel bekommen und unsere Personen per PersonListItem anzeigen.

Hierfür benötigen wir Properties und Aggregations.

Metadata definieren

Wir benötigen folgende Property für unseren Titel:

  • title (string) – default: Persons

Zusätzlich möchten wir noch PersonListItems aggregieren:

  • items (PersonListItem) – multiple: true

CSS Styling

per list-style: none sagen wir, dass unsere Personenliste keine Aufzählungszeichen haben sollte.

Dann selektieren wir alle Items mit einem ungeraden Index und färben diese grau ein.

Zusätzlich geben wir noch ein seitliches margin von 15 Pixel.

Renderer definieren

In unserem Renderer wollen wir den Titel der Personenliste und aggregiert die PersonListItems ausgeben.

1: div öffnen

Gleich wie beim PersonListItem öffnen wir einen div-Container und vergeben CSS-Klasse und Control-Data.

2: Titel ausgeben

Der Titel wird innerhalb eines h2-Tags ausgebeben.

3: Items-Aggregation rendern

Per getAggregation(„items“) können wir uns den Inhalt der Items-Aggregation in einem Array speichern. Dieses Array laufen wir durch und erstellen für jeden Eintrag ein neues li-Tag mit der myPersonListItemSelector-Klasse. Dieses bekommt ihren Inhalt per renderControl()-Funktion, welche die render-Methode des übergebenen Controls aufruft. In unserem Fall ist das das PersonListItem.

4: alles schließen

 Abschließend fügen wir noch die schließenden ul- und div-Tags hinzu.

Jetzt sind unsere Custom-Controls fertig und können in den Views verwendet werden.

Controls verwenden

Die beiden Custom Controls können nun in unserer View verwendet werden.

1: Namespace einbinden

Um Controls aus einem anderen Namensraum einzubinden, muss für diesen ein Alias angelegt werden. Wir verweisen hier auf unseren Folder, wo wir die Controls angelegt haben.

2: PersonList einfügen

Wir legen eine neue PersonList an. Dort vergeben wir die title-Property und legen eine items-Aggregation an.

3: PersonListItems hinzufügen

In der items-Aggregation legen wir nun mehrere PersonListItems an. Das würde natürlich auch über Aggregation-Binding in der PersonList per Model funktionieren.

Wir legen aber z.B. 3 PersonListItems an und vergeben allen Properties Werte. Zusätzlich definieren wir eine Funktion für unser sayHello-Event.

Dieses müssen wir dann noch im zugehörigen Controller ausprogrammieren.

Also definieren wir eine neue Methode namens onSayHelloHandler, wo wir den Eventparameter message auslesen und per alert anzeigen.

App ausführen

Unsere App ist nun funktionsfähig und zeig unsere Custom-Controls richtig an.

Wir sehen nun unseren Titel und 3 darunterliegende Personen, die farblich abwechselnd hinterlegt sind.

Zusammenfassung

In 2 Blogteilen haben wir nun alles Notwendige gelernt, um mit dem Entwickeln von eigenen Custom-Controls durchzustarte, falls die Anforderungen nicht durch das umfangreiche SAPUI5 Controls-Paket abgedeckt wird.

Eine weitere Vorgehensweise könnte sein, meine eigenen Custom-Controls in eine UI5-Library/Git Repo zu packen und zu deployen. Dann kann ich diese in weiteren Applikationen verwenden und kann mir meine eigene, umfangreiche Library bauen.

Nur sollte man nicht für jede Anforderung ein neues Control entwickeln oder ein bestehendes zu erweitern. Man sollte so gut und so weit wie möglich mit dem arbeiten, was einem das SAPUI5 Framework zur Verfügung stellt und sich so weit wie möglich in die Materie einarbeiten.

Wer also gerne mehr bezüglich UI5 wissen möchte, kann sich gerne unseren zahlreichen Blogs bedienen.

Die interessantesten für das Controls-Thema sind:

Vielen Dank für das Lesen dieser Blogreihe und wenn Fragen auftreten, können sie gerne in den Kommentaren gestellt werden.

SAPUI5 Custom Controls – Teil 1

SAPUI5 Custom Controls – Teil 1

Das Konzept

In SAP UI5 ist es möglich, bestehende Controls zu erweitern, sobald die normalen Funktionalitäten eines Controls nicht mehr ausreichen. Ebenso lassen sich ganz neue Controls erstellen, um den wachsenden Anforderungsschwierigkeiten gewachsen zu sein.

Leider weicht man jedoch dann vom Fiori-Standard ab und ist nicht mehr Guideline-Konform. Jedoch kann es trotzdem sein, dass man eine Aufgabenstellung zu bewältigen hat, die mit dem Standard nicht abzubilden ist.

Genau hier tritt das Custom-Control-Konzept ins Spiel.

 

Der Aufbau

Grundsätzlich ist ein UI5 Control nichts anderes als ein Modul, das zur Laufzeit dynamisch nachgeladen wird. Ein solches Modul wird mit sap.ui.define erstellt und kann anschließend angesprochen werden.

Des Weiteren wird ein UI5 Control immer als HTML-Element gerendered. Wie genau das aussieht, wird immer im Control-spezifischen render-Methode über den RenderManager definiert. Diese Methode wandelt das Custom Control in die HTML-Elemente um, wie es der Entwickler gerne haben möchte. So wird zum Beispiel beim Rendern einer sap.m.Table eine HTML-table mit td- und tr-Elementen in das DOM eingetragen und mit SAP-spezifischen CSS-Klassen gestyled.

Der Aufbau von Eigenschaften eines Controls wird immer in seinen Metadaten definiert. Ein Control hat ein dementsprechendes metadata-Object, wo Properties, Events, Aggregations und Associations definiert werden. Diese können dann in der View und über den Controller angesprochen werden.

Dies sieht wie folgt aus:

Sobald ich die Methode extend von dem sap.ui.core.Control aufrufe, erstelle ich ein neues Control. Wenn ich das extend von einem bestehenden Control aufrufen, z.B. dem sap.m.Button, würde ich dieses erweitern.

Nach dem ich nun ein neues Control erstellt habe, kann ich die Metadaten definieren:

 

Properties

Control-Properties bekommen ein Object zugewiesen, in dem ich den Datentyp zuweise und gegebenenfalls einen Default-Wert hinterlege.

Folgende Einstellungen kann ich also vornehmen:

  • type – Default type ist string.
  • defaultValue

Zulässige Datentypen für die type-Property sind:

  • string
  • int
  • float
  • string[]

Ich kann auch eigene Konfigurationsproperties innerhalb meiner Property erstellen und diese dann zur Datenverarbeitung nutzen.

Aggregations

Aggregations werden mit 3 verschiedenen Konfigurationen erstellt:

  • type – Default type ist sap.ui.core.Control.
  • multiple – Default ist true.
  • singularName
  • visibility 

Type gibt an, welches Control ich gerne aggrigieren möchte. Manchmal sind solche Aggregation-Controls nicht Module die von sap.ui.core.Control erben, sondern von der abstrakten Klasse sap.ui.core.Element. Sie haben daher keinen eigenen Renderer. Das Rendering von diesen Controls übernimmt das Control, das diese Elemente aggregiert. Mein Custom Control kann natürlich auch andere Custom Controls aggregieren.

Mit multiple: true kann ich sagen, ob meine Aggregation 0 bis N Controls aggregieren kann, oder ob nur ein Control in meiner Aggregation zugelassen wird.

Die Property singularName gibt einen String an, der für automatisch generierte Methoden eingefügt werden soll, um z.B. ein neues Item in die Items-Aggregation einzufügen.

Per visibility-Property lege ich fest, ob meine Aggregation in der View benutzt werden kann und ob sie für den Entwickler sichtbar sein sollte. Wenn die visibility auf Verbergen sein sollte, dann muss die Value „hidden“ vergeben werden.

Associations

Mit Associations können Controls miteinander verknüpft werden, bzw. es kann eine Abhängigkeit erstellt werden. Das bekannteste Beispiel ist die labelFor-Property/Association vom sap.m.Label.

Associations haben den gleichen Konfigurations-Aufbau wie Aggregations. Sie haben daher auch:

  • type
  • multiple
  • singularName
  • visibility

Events

Controls können Eventhandler für bestimmte HTML-Events definieren. Für jedes selbstdefinierte Event werden automasich folgende Funktionen erstellt:

  • attach<Name>
  • detach<Name>
  • fire<Name>

Somit würde ich z.B. für mein Custom Event saveForm die Funktionen attachSaveForm, detachSaveForm und fireSaveForm automatisch generiert bekommen.

Sobald ich ein Event erstellt habe, muss ich nur mehr den dementsprechenden HTML-Eventhandler definieren und dort die fire-Methode meines Events aufrufen. Für die Event-Handler-Implementierung erstelle ich innerhalb meines Controls eine neue Methode, die genau so heißt wie das dementsprechende HTML-Event.

Events können auch Parameter definieren, die ich bei einem fireEvent befülle.

myEvent: {

    parameters: { wasClicked: {type: „boolean“}}

}

Zusätze

Zusätzlich zu den 4 Basis-Konfigurationen stehen noch mehr Optionen zur Verfügung.

So kann ich

  • dnd – Drag and Drop
  • designtime – Design

zusätzlich definieren und konfigurieren. Auch kann ich das Metadaten-Objekt um eigene Einstellungen erweitern.

 

Coding

Die wichtigste Methode unseres Controls ist die onInit-Methode. Diese wird beim Initialisieren unseres Controls aufgerufen und dient mir als Startmöglichkeit.

Weitere Funktionen können ganz normal als Objekt-Methoden eingefügt werden. Hier sollte aber auf das Naming der Methoden geachtet werden, damit es keine Überschneidungen zu automatisch generierten Methoden kommt.

Mit Hilfe der automatisch generierten Set– und Get-Methoden meiner Properties kann ich jetzt auf diese zugreifen.

 

Render Manager

Nachdem ich nun die Metadaten und das Coding fertig habe, muss ich das Control auch dementsprechend anzeigen.

Hierfür dient der Render Manager in der renderer-Methode meines Controls. Mit dessen Hilfe kann ich in das DOM eingreifen und mein Control platzieren. Der Render Manager kommt in der renderer-Methode meines Controls zum Einsatz. Benutzt wird normaler HTML-Code, um unser Control in das DOM zu bringen.

Normalerweise wird die renderer-Methode in ein eigenes File ausgelagert.

Die wichtigsten Funktionen des RM’s sind:

  • openStart – Erstellt ein neues öffnendes Element <…
  • openEnd – Schließt das öffnende Element <…>
  • close – Schließt das Element </…>
  • write – Schreibt HTML-Code
  • writeControlData – Schreib die Conrol-ID in das DOM
  • renderControl – Ruft die renderer-Methode eines aggregierten Controls auf
  • class – Fügt eine CSS-Klasse dem HTML-Element hinzu
  • attr – Fügt dem Element ein Attribut mit Value hinzu

Zusammenfassung

Wir sehen, dass das Erstellen eines Custom-Controls einige Möglichkeiten beinhaltet. Wir können Properties, Aggregations, Events und Associations definieren. Zusätzlich benötigen wir noch die renderer-Methode mit dem RenderManager, um unser Control in das DOM zu bringen und anzuzeigen.

Im nächsten Blog-Part 2 werden wir ein Beispiel-Control entwickeln und uns im Detail die Implementierung ansehen.

Falls Fragen auftauchen, können sie gerne unten in den Kommentaren gestellt werden.  Ich freue mich auf Feedback.