JSON Schema und JSON Forms – formularbasierte Web-Applikationen
Fast jeder Entwickler hat schon einmal eine formularbasierte Oberfläche entwickelt. Während das Resultat für Endnutzer meist sehr einfach aussieht, steckt in der Praxis überraschend viel Coding-Aufwand selbst hinter sehr einfachen Eingabe-Formularen. Der Teufel steckt hierbei im Detail. Ein einfaches Textfeld erfordert nicht nur das Erzeugen von Label und Textbox, sondern mindestens auch das Binden der Daten und eine Eingabevalidierung. Spätestens, wenn mehrere Controls in einem Layout zusammengefasst werden oder Eingaben regelbasiert das Formular verändern, sind schnell einige Stunden Arbeit fällig.
In diesem Artikel stellen wir das Open Source-Framework JSON Forms vor, welches die Entwicklung von formularbasierten Weboberflächen vereinfacht. Formulare werden dabei in einem einfachen deklarativen JSON Format beschrieben und vom Framework gerendert (bspw. mit Angular). Das Framework JSON Forms ist unter der Open Source-Lizenz MIT frei zur Anwendung verfügbar [1].
Deklarative Formulare
Viele Business-Anwendungen sind auf die Ein- und Ausgabe, sowie die anschließende Verarbeitung von Daten fokussiert. Beispiele solcher datenzentrischer Anwendungen finden sich durchgängig in fast allen Branchen, beispielsweise Warenwirtschaftssysteme, CRM-Systeme oder ERP-Systeme. Unabhängig von der konkreten Domäne werden die entsprechenden Daten häufig in formularbasierten Ein- und Ausgabemasken dargestellt. Diese Masken zeigen den Inhalt einer oder mehrerer Entitäten der Anwendung und deren Attribute an und erlauben die Bearbeitung der Daten. In der Praxis verursacht die manuelle Entwicklung solcher Formulare selbst bei der Verwendung von mächtigen Frameworks wie Angular unnötig viel Aufwand.
Dies liegt im Wesentlichen in der Ausdrucksmächtigkeit der verwendeten Technologien begründet. Sprachen wie HTML oder JavaScript oder Frameworks wie Angular unterstützen die Entwicklung von völlig beliebigen Oberflächen und Anwendungen, sie sind nicht auf Formulare fokussiert. Ihre Verwendung für die Entwicklung von Formularen erfordert viele, teilweise umständliche und manuelle Schritte.
Deklarative Frameworks wie JSON Forms bieten eine speziell auf die Beschreibung von Formularen angepasste deklarative Sprache. Mit dieser lässt sich ein Formular deutlich effizienter und dazu noch technologieneutral beschreiben. Die tatsächliche Oberfläche wird basierend auf dieser Beschreibung von einer Rendering-Komponente erzeugt (im folgenden "Renderer"). Damit ist kein manuelles Programmieren der Formulare mehr notwendig. Oberflächen können damit effizienter und vor allem auch mit weniger Vorwissen umgesetzt werden.
Der Renderer übernimmt dabei nicht nur das reine Anzeigen von Formularen, sondern auch das Binden der Daten, die Validierung, regelbasierte Abhängigkeiten zwischen Eingabefeldern und vieles mehr. Natürlich verbirgt sich hier wieder ganz konkreter Code in HTML und JavaScript. Sind Anpassungen notwendig, lassen sich die einzelnen Teile des Renderers mit einigen Kenntnissen der jeweiligen Web-Technologie nach eigenen Wünschen anpassen. Im folgenden Abschnitt stellen wir den Ansatz deklarativer Formulare mit dem Open Source-Framework JSON Forms an einem konkreten Beispiel vor.
JSON Forms
Im Folgenden wird ein vereinfachtes Beispiel verwendet, um die Funktionsweise von JSON Forms im Detail zu erläutern. Es handelt sich dabei um ein Formular für die Verwaltung von Personen. Im Beispiel enthält die Entität "Person" lediglich die folgende vier Attribute, in echten Anwendungen sind Datenmodelle natürlich meist komplexer:
- Name: String (Pflichtfeld)
- DateOfBirth: Datum
- Gender: Enumeration (Male/Female)
- Rating: Integer (von 1-5)
Die deklarative Entwicklung von Oberflächen beginnt zunächst mit einer formalen Beschreibung der anzuzeigenden Daten. Diese ist getrennt von der eigentlichen Beschreibung der UI. Durch diese einfache Modularität kann die Spezifikation der Daten leicht wiederverwendet werden, beispielsweise für eine Validierung im Backend, oder für eine weitere UI mit anderem Aufbau. Zur Definition der Daten wird das Standard-Format "JSON Schema" verwendet. Die Definition des Attributes name findet sich als Beispiel in Listing 1.
Listing 1
{ type: 'object', properties: { name: { type: 'string' } }, required: ['name'] }
Zur Beschreibung der eigentlichen Oberfläche führt JSON Forms ein zweites Format ein, das "UI Schema". Das UI Schema legt fest, wie die im JSON Schema definierten Daten im Formular angezeigt werden sollen. Die Information aus dem Datenschema wird dabei referenziert und damit wiederverwendet. Listing 2 zeigt ein minimales UI Schema, welches das zuvor definierte Attribut name in einer Spalte anzeigt.
Listing 2
{ type: 'VerticalLayout', elements: [ { type: 'Control', scope: { $ref: '#/properties/name' } } ] }
Durch das einfache Hinzufügen weitere Attribute nach dem gleichen Prinzip kann das Formular nun beliebig ausgebaut werden. Um es dann tatsächlich in der eigenen Webanwendung anzuzeigen, reicht der folgende HTML-Code.
Listing 3
<jsonforms schema='vm.schema' uischema='vm.uischema' data='vm.data'></jsonforms>
Die Variable vm.schema zeigt dabei auf das eben definierte Datenschema und vm.uischema auf das UI Schema. Die Variable vm.data ist ein JSON Object Person, welches durch das Formular befüllt wird. Enthält das Datenobjekt bereits Werte, werden diese direkt im Formular angezeigt. Mit diesen einfachen Schritten erzeugt der Renderer von JSON Forms dann automatisch das in Abb.1 gezeigte Formular.
Dabei übernimmt der Renderer einiges an manueller Arbeit. Zunächst erzeugt er basierend auf den spezifizierten Datentypen entsprechende Controls. So ist beispielsweise das Dropdown Control für das Attribut Gender schon automatisch mit den möglichen Werte befüllt. Ändern sich diese im Schema, passt sich die UI ohne zusätzliche Schritte daran an. Die angezeigten Controls sind bereits an das zugrundeliegende JSON-Objekt gebunden. Eingaben lassen sich damit leicht verarbeiten, beispielsweise an einen REST-Service übertragen. Bei der Eingabe werden die Daten ohne weiteres Zutun live gegen das Datenschema validiert. Beispielsweise zeigt die Form in der Abbildung an, dass das Attribut Rating nicht dem zuvor definierten Wertebereich (1-5) entspricht.
Nicht zuletzt stellt der Renderer die Controls in einem Standard-Layout dar, da hier bisher nichts genaueres spezifiziert wurde.
Layouts
Wie im letzten Abschnitt anhand eines einfachen Beispiel verdeutlicht, reicht bereits eine sehr einfache Definition von Datenschema und UI-Schema aus, um ein voll funktionstüchtiges Formular zu erstellen. Das wäre natürlich gerade in umfangreichen Formularen wenig wert, würden die einzelnen Controls immer nur stur in einer vertikalen Liste angeordnet. Zu diesem Zweck unterstützt der deklarative Ansatz von JSON Forms natürlich auch die Definition von Layouts für Formulare. Auch die Layout-Informationen werden dabei im UI-Schema spezifiziert. Das Format folgt einer für Layouts typischen hierarchischen Struktur, ähnlich wie HTML. Controls können dabei in Layouts enthalten sein, welche selbst wiederum in eine Hierarchie eingeordnet werden.
Listing 4 definiert ein Layout für das vorherige Beispiel. Dabei sind zwei Group-Elemente in einem HorizontalLayout angeordnet. Die Group-Elemente enthalten jeweils zwei Controls.
Listing 4
{ "type": "HorizontalLayout", "elements": [ { "type": "Group", "label":"Group1", "elements": [ ... ] }, { "type": "Group", "label":"Group2", "elements": [ ... ] } ] }
Mit dieser einfachen Layoutinformation ordnet der Renderer die Controls nun wie in Abb.2 an.
Der wesentliche Unterschied zu einer mächtigen Sprache wie HTML oder zu UI-Toolkits wie JavaFX ist hierbei, dass das UI-Schema explizit auf Formulare ausgelegt ist. Diese Fokussierung erlaubt eine Vereinfachung der Mächtigkeit und damit eine Verringerung der Komplexität. Durch die einfache Hierarchie lassen sich auch komplexe Layouts übersichtlich definieren. Beispielsweise können ganze Gruppen von Controls via Drag-and-drop verschoben werden (mehr dazu im Abschnitt "Tooling").
JSON Forms unterstützt von Haus aus eine ganze Reihe von Layouts, beispielsweise lassen sich Formulare auch in Tabs unterteilen. Zusätzlich ist der Renderer erweiter- und anpassbar, um das Framework auf eigene Layoutwünsche anzupassen.
Anpassungen
JSON Forms liefert einen fertigen und ohne Anpassungen nutzbaren Renderer mit aus. Das ermöglicht einen schnellen Start und das effiziente Erstellen eigener Formulare. In umfangreichen Anwendungsbeispielen ist es jedoch durchaus typisch, dass die eigenen Anforderungen über den Standard des Frameworks hinausreichen. Beispielsweise kann eine eigene Anwendung einen ganz speziellen Control-Typ für ein bestimmtes Attribut (oder eine Gruppe von Attributen) erfordern.
Aus diesem Grund ist JSON Forms anpassbar und erweiterbar. Konkret besteht der Renderer technisch gesehen aus vielen kleinen Komponenten. Jeder dieser Teilrenderer ist für ein bestimmtes Konzept aus dem UI-Schema zuständig. Beispielsweise gibt es eine Komponente für ein Integer Control, eines für das VerticalLayout, usw. Nun lassen sich zusätzliche Renderer registrieren, welche unter bestimmten Bedingungen alternativ zu den Standard-Renderern verwendet werden. Damit lässt sich das Verhalten des Frameworks flexibel anpassen. Die Registrierung kann basierend auf dem Datentyp geschehen, damit verändert man das Verhalten beispielsweise aller Integer Controls. Alternativ lassen sich jegliche Bedingungen festlegen, bis hin zu einem ganz spezifischen Attribut. Im folgenden Beispiel wollen wir einen eigenen Renderer lediglich für das Attribut rating der Entität registrieren. Dazu sind zwei Teilkomponenten notwendig.
Zum Einen muss ein Template definiert werden, dass das Aussehen des Renderers wie in Listing 5 dargestellt beschreibt.
Listing 5
<jsonforms-control> <uib-rating id="{{vm.id}}" readonly="vm.uiSchema.readOnly" ng-model="vm.resolvedData[vm.fragment]" max="vm.max()" min="vm.min()"> </uib-rating> </jsonforms-control>
Zum Anderen muss die Logik des Renderers definiert werden und dieser registriert werden. Die Logik des Renderers wird als AngularJS Directive definiert und der Element-Name wird dann bei JSON Forms mit einem Tester registriert, siehe Listing 6. Das Ergebnis ist in Abb.2 zu sehen.
Listing 6:
angular.module('MiHexample') .directive('ratingControl', function() { return { restrict: 'E', controller: ['BaseController', '$scope', function(BaseController, $scope) { var vm = this; BaseController.call(vm, $scope); vm.max = function() { return 5; }; vm.min = function() { return 1; }; }], controllerAs: 'vm', templateUrl: './renderer/rating.control.html' }; }) .run(['RendererService', 'JSONFormsTesters', function(RendererService, Testers) { RendererService.register('rating-control', Testers.and( Testers.uiTypeIs('Control'), Testers.schemaPropertyName('rating') ), 10); }]);
Mit eigenen Renderern lässt sich also leicht das Verhalten von JSON Forms auf eigene Bedürfnisse anpassen. Das Framework erlaubt dadurch ein iteratives Vorgehen. In einer ersten Version liefert der Default-Renderer bereits eine benutzbare Oberfläche, welche dann Schritt für Schritt verfeinert werden kann. Dabei müssen lediglich spezifische Komponenten selbst implementiert werden. Ein eigener Renderer lässt sich auf Grund der modularen Architektur dann bequem innerhalb des Projekts oder auch in anderen Projekten wiederverwenden.
Mehr?
Im Artikel wurden die Grundlagen von JSON Forms vorgestellt. Das Framework bietet allerdings noch eine Reihe von zusätzlichen Features, welche für eine echte Anwendung typischerweise in Formularen benötigt werden. Ein Beispiel dazu ist ein regelbasiertes Verhalten von Formularen. Dabei sollen bestimmte Teile des Formulars nur bearbeitbar oder sogar nur sichtbar sein, wenn zuvor bestimmte Bedingungen eintreten. Auch diese Art von UI-Anforderungen lassen sich im UI-Schema beschreiben und werden vom Renderer entsprechend berücksichtigt [1].
Dort finden sich auch weitere Features für komplexere Formulare, wie beispielsweise Master-Detail-Ansichten oder spezielle Datentypen wie Arrays.
In den vorherigen Abschnitten wurden Teile des UI-Schemas vorgestellt, mit dem sich in JSON Forms Oberflächen beschreiben lassen. Dabei wurde die "Roh-Form" des Schemas referenziert, also direkt das JSON-Format. Dieses einfache Format ist universell les- und editierbar und lässt sich damit mit jedem Editor oder jeder IDE gut bearbeiten.
Noch komfortabler wird die Erstellung und Bearbeitung eines UI Schemas mit einem dafür ausgelegten Editor. Hier lassen lässt sich beispielsweise die Struktur eines Formulars via Drag-and-drop bearbeiten, Controls aus dem Datenschema generieren, sowie eine Preview anzeigen. Für JSON Forms gibt es zwei Varianten eines solchen Toolings. Zum einen gibt es eine Beta-Version eines Online-Editors, der die erwähnten Features in einer Webanwendung bereitstellt. Als zweite Alternative dient die bereits seit vielen Jahren entwickelte Desktop-Version von JSON Forms: EMF Forms [2]. EMF Forms funktioniert nach dem gleichen Prinzip wie JSON Forms, es bietet ein UI-Schema (in EMF Forms "View Modell") und basiert statt auf JSON Schema auf der Modellierungssprache EMF. UI-Schema und View Modell sind dabei weitestgehend kompatibel, insbesondere lassen sich View Modelle in UI-Schemata umwandeln. Damit lässt sich das mächtige Tooling von EMF Forms für die Erstellung von UI-Schemata verwenden [3].
Fazit
Mit JSON Forms kann man sehr effizient Formulare entwickeln, ohne dabei direkt manuell Code schreiben zu müssen. Das Framework ist dabei trotzdem flexibel und lässt sich mit eigenen Renderern auf spezifische Bedürfnisse anpassen. Damit verringert JSON Forms deutlich den notwendigen Aufwand für die Implementierung und vor allem auch Wartung von Oberflächen. Diese erzeugen gerade in Geschäftsanwendungen durch Zusatzanforderungen wie regelbasierte Sichtbarkeiten oder Validierung meist signifikante Aufwände eines Projekts.
Im Gegensatz zu manuell geschriebenen Code bietet JSON Forms neben dem Zeitvorteil weitere Vorteile. Das UI-Schema selbst ist nicht von einer konkreten UI-Technologie oder einem konkreten JavaScript-Framework abhängig. Gerade im volatilen Web-Umfeld ändern sich verwendete Technologien oft nach kurzer Zeit. Dies würde bei einer manuellen Lösung eine Neu-Implementierung der UI erfordern. Im Falle von JSON Forms müssen lediglich die Renderer auf eine neue Technologie umgestellt werden. Der Standard-Renderer von JSON Forms ist in Version 1.0 mit Angular, in der Version 2.0 Framework-neutral mit Web-Components umgesetzt. Mit EMF Forms existiert sogar ein kompatibles Framework auf der Desktop-Seite. Einmal beschriebene Formulare lassen sich so auf ganz unterschiedlichen Technologie-Stacks anzeigen.
Nicht zuletzt lässt sich durch einen deklarativen Ansatz wie JSON Forms die Entwicklung von Oberflächen näher an Kunden, respektive Nutzer, bringen. Das gelingt zum einen durch ein sehr schnelles Erzeugen einer ersten UI basierend auf definierten Attributen, welches bereits demofähig ist. Zum anderen lassen sich Anpassungen, gerade im Layout quasi live durchführen.
Komplexere Änderungen, wie beispielsweise die Anpassung eines Control, erfordern natürlich nach wie vor Implementierungsaufwände. Diese lassen sich jedoch iterativ und vor allem an zentraler Stelle (im Renderer) durchführen. Dies vereinfacht die Weiterentwicklung und vor allem auch langfristige Wartung von Formularen.
JSON Forms und EMF Forms sind unter der Open Source-Lizenz EPL bzw. MIT License frei verfügbar und somit auch im kommerziellen Umfeld kostenlos einsetzbar.