Blog

Master-Detail-Implementierung für RESTful-Dienste mit JQuery

Maarten Winkels

Aktualisiert Oktober 22, 2025
8 Minuten

Erholsamer Flug" In zwei früheren Beiträgen haben wir gesehen, wie man mit JBoss AS 7 RESTful-Anwendungen entwickelt. Am Ende des zweiten Blogs haben wir ein generisches REST-Client-Tool verwendet, um einige RESTful-Webdienste auszuführen. Natürlich würden wir lieber eine benutzerdefinierte UI-Anwendung als Client für unsere Dienste erstellen, damit ein Benutzer einfach auf die Daten zugreifen und sie manipulieren kann. In diesem Blog erstellen wir einen REST-Client, der auf dem Master-Detail-Prinzip basiert.

REST und Master-Detail

Ein RESTful Webservice, wie er in den vorangegangenen Beiträgen entwickelt wurde, ist im Wesentlichen eine editierbare Sammlung. Das macht ein Master-Detail-Design zu einem erstklassigen Kandidaten für die darauf aufbauende Benutzeroberfläche: Die Benutzeroberfläche zeigt die gesamte Sammlung in der Master-Ansicht an und ein einzelner Eintrag in der Sammlung wird in der Detail-Ansicht editierbar gemacht.

Master-Detail

Wie implementieren wir das? Nun, die Webservices erzeugen Daten im JSON-Format. Ein Client muss diese Daten konsumieren und sie in einer Benutzeroberfläche anzeigen. Ein JavaScript-Client ist dafür eine sehr gute Wahl, denn JSON ist eine Art "natives" Format für JavaScript. Natürlich sind auch andere Optionen denkbar, z.B. könnten Sie einen Client mit Adobe Flex oder JavaFX entwickeln. Ein Vorteil der Verwendung von einfachem HTML und JavaScript wäre, dass es viele Anwendungen (Webbrowser) gibt, die diese Sprachen verstehen und zur Ausführung des REST-Clients verwendet werden können. Es gibt eine Reihe von Frameworks, die für die Entwicklung eines REST-Clients in JavaScript verwendet werden können. Eine beliebte Wahl ist BackBone.js mit Mustache. Für diesen Blog habe ich mich entschieden, den Client als jQuery-Plugin zu entwickeln. jQuery ist eine universelle JavaScript-Bibliothek mit vielen Funktionen. Wir werden die Templating-Funktion verwenden, um die Daten anzuzeigen.

Verwendung der jQuery-Vorlage für die Master-Ansicht

Schauen wir uns zunächst die Master-Ansicht an. Dies ist eine Tabelle mit einer Zeile für jedes Buch, in der die ID und der Titel angezeigt werden.

Master-Tabellematser-Tisch

Was wir dafür brauchen, ist eine einfache jQuery-Vorlage, die für jedes Buch eine einzelne Zeile anzeigt, und einen geeigneten Container, in dem die Zeilen platziert werden können. [html] <script id="bookRowTemplate" type="text/x-jquery-tmpl"> <tr> <td class="idCol"><a href="#" data-id="${id}" class="bookSelect">${id}</a></td> <td class="titleCol">${title}</td> <td><a href="#" data-id="${id}" class="bookDelete">löschen</a></td> </tr> </script> [/html] Die jQuery-Vorlage kann ein Skript-Tag in der Kopfzeile der Seite sein. Es sollte den Typ text/x-jquery-tmpl und eine geeignete ID für die Referenzierung haben. Die Eigenschaften der Objekte, die wir anzeigen möchten (in diesem Fall die ID und der Titel des Buches), können mit der Notation ${...} eingefügt werden. [html] <Tabelle> <Bildunterschrift>Bücher</caption> <thead> <tr> <th class="idCol">Id</th> <th class="titleCol">Titel</th> </tr> </thead> <tbody id="booksContainer"></tbody> </table> [/html] Das Container-Element ist in diesem Fall ein tbody mit id="booksContainer". Das Muster der hier verwendeten IDs wird später noch deutlicher werden.

Laden der Daten und Ausfüllen der Vorlage

Wie laden wir nun die Daten herunter und verwenden die Vorlage, um sie anzuzeigen? [javascript] function loadList (Optionen) { doAjaxCall('GET', options.url, null, function(data) { useTemplate(options.rowTemplate, data, options.rowsContainer); }); } function doAjaxCall(type, url, data, callback) { $.ajax({ Typ: Typ, url: url, dataType: "json", Daten: Daten, Erfolg: Rückruf }); } function useTemplate(template, data, container) { wenn (Vorlage) { container.empty(); template.tmpl(data).appendTo(container); } } [/javascript] Die Funktion loadList lädt die Daten und zeigt sie in der Master-Ansicht an. Sie verwendet die Funktion doAjaxCall, um das asynchrone Laden zu starten. Dies ist lediglich ein Wrapper um die standardmäßige jQuery ajax-Funktion, aber sie wird an vielen weiteren Stellen verwendet werden. Wenn der Ajax-Aufruf erfolgreich zurückkommt, wird die Funktion useTemplate verwendet, um die zurückgegebenen Daten in der Vorlage anzuzeigen. Das options-Objekt, auf das an einigen Stellen verwiesen wird, ist ein Container für Konfigurationsoptionen für den Client, die später besprochen werden. Die hier relevanten Konfigurationsoptionen sind: [javascript] var options = { url: 'books/', rowTemplate: $('#bookRowTemplate'), rowsContainer: $('#booksContainer') }; [/javascript] Das JSON, das zur Anzeige geladen wird, ist unten dargestellt. In diesem Fall handelt es sich um eine Liste von Objekten, wobei jedes Objekt ein Buch mit einer ID und einem Titel ist. Da es sich um eine Liste handelt, wird die Vorlage auf jedes Element angewendet und der resultierende HTML-Code wird verkettet.

get-data

Auswählen und Löschen von Objekten in der Liste

Die Vorlage für jede Zeile enthält zwei Links, einen zum Auswählen eines Artikels, den anderen zum Löschen. Wie funktionieren diese? [javascript] // opts.selectLinks = $('.bookSelect'); opts.selectLinks.live('click', function (event) { var id = $(this).data('id'); doAjaxCall('GET', url(opts.url, id), null, function(data) { useTemplate(opts.entryTemplate, data, opts.entryContainer); }); event.preventDefault(); }); //opts.deleteLinks = $('.bookDelete'); opts.deleteLinks.live('click', function (event) { var id = $(this).data('id'); doAjaxCall('DELETE', url(opts.url, id), null, function(data) { doResetAndReload(opts); }); event.preventDefault(); }); function url(url, id) { return url + id + '/'; } [/javascript] Handler für die Klick-Ereignisse werden mit jQueries an diese Links angehängt live Funktion. Aus der Dokumentation

Hängen Sie einen Handler an das Ereignis für alle Elemente an, die dem aktuellen Selektor entsprechen, jetzt und in Zukunft.

In unserem Fall erzeugt die Zeilenvorlage Klassenattribute für die Links, die zum Anhängen des Handlers verwendet werden können. Für beide Operationen benötigen wir die ID des Buches, das wir auswählen oder löschen möchten. Um den Zugriff zu erleichtern, erzeugt die Vorlage ein Datenattribut(data-id) in den Links. In den Event-Handlern wird die ID aus dem Knoten abgerufen und für einen weiteren Ajax-Aufruf verwendet. Für die Lösch-Links wird eine DELETE-Methode aufgerufen und bei Erfolg wird die Benutzeroberfläche zurückgesetzt und die Liste neu geladen. Für die Select-Links müssen wir die Detaildaten des Elements in einem Eingabeformular anzeigen.

Die Detailansicht als Eingabeformular

Das Eingabeformular sollte die Daten für ein Element in der Liste anzeigen (in unserem Fall ein Buch) und es sollte dem Benutzer ermöglichen, die Daten im Formular an einen Webdienst zu POSTen oder zu PUTen. Das statische HTML ist recht einfach: [html] <div id="bookContainer"> Buch: <form id="bookForm"> <fieldset id="bookEntryContainer"> </fieldset> <input type="zurücksetzen" value="Abbrechen" /> <input type="submit" value="Speichern" /> </form> </div> [/html] Das Formular hat nur ein id-Attribut und keine Aktion oder Methode. Dies wird von dem Javascript-Handler für das Absenden des Formulars übernommen. Die Schaltflächen Submit und Cancel sind statisch und Standard. Der interessante Teil ist das Fieldset, das die Elemente enthält, die von der Vorlage gerendert werden. [html] <script id="bookEntryTemplate" type="text/x-jquery-tmpl"> <p>Id: ${id}</p> {{if id}} <input id="idField" name="id" type="hidden" value="${id}"/> {{/if}} <label for="titleFld">Titel:</label> <input id="titleFld" name="title" value="${title}"/> </script> [/html] In dieser Vorlage gibt es eine kleine Logik, um die Fälle für neue und bestehende Bücher zu behandeln. Ein vorhandenes Buch kann an seiner id-Eigenschaft erkannt werden. Wenn die id-Eigenschaft ausgefüllt ist, sollte sie gesetzt und an den Webservice gesendet werden, indem das versteckte Eingabefeld hinzugefügt wird. Die JavaScript-Verarbeitung der Formularereignisse ist etwas komplizierter. [javascript] opts.entryForm.submit(function (event) { var form = $(this); var data = form.serialize(); var id = $('#'+opts.entryIdField).val(); if (id) { doAjaxCall('PUT', url(opts.url, id), data, function(data) { doResetAndReload(opts); }); } sonst { doAjaxCall('POST', opts.url, data, function(data) { doResetAndReload(opts); }); } event.preventDefault(); }); opts.entryForm.bind('reset', function (event) { onSelect(opts, null); event.preventDefault(); }); [/javascript] Wenn Sie das Formular abschicken, möchten wir den Inhalt als Formulardaten über einen Ajax-Aufruf senden. Wenn die ID nicht festgelegt ist, wird eine POST-Methode aufgerufen, um eine neue Instanz zu erstellen. Wenn die ID gesetzt ist, sollte eine PUT-Methode aufgerufen werden, bei der die ID an die URL angehängt wird. In beiden Fällen wollen wir die Benutzeroberfläche zurücksetzen und die Liste bei einer erfolgreichen Rückkehr neu laden.

Das Sahnehäubchen auf dem Kuchen: Erweiterung der Optionen

Dies ist im Grunde der gesamte Code für diesen Master-Detail-basierten REST-Client mit jQuery-Vorlagen. Mit diesem Code können Sie einfache CRUD-Operationen für einfache Bücher durchführen, wie in den Screenshots unten gezeigt.

Szenario1 Szenario2 Szenario3

Was ist nun mit den Optionen, von denen Sie immer wieder gehört haben? Ich habe den Client so konzipiert, dass ich mehrere REST-Clients für verwandte Sammlungen auf derselben Seite verwenden kann. Deshalb wollte ich einen Mechanismus, der es mir ermöglicht, Folgendes zu tun [javascript] new RestClient({Name:'book'}); new RestClient({Name:'author'}); [/javascript] und der Client würde herausfinden, welche Vorlagen, welche Container und welche Formulare für Anzeige- und Eingabezwecke zu verwenden sind. Dies wird durch die Verwendung von Konfigurationen erreicht, die um den Wert für andere Optionen erweitert werden. [javascript] RestClient = function (options) { var opts = $.extend({}, RestClient.defaults, options); expand(opts); ... }; RestClient.defaults = { Plural: '{name}s', baseUrl: 'services', url: '{baseUrl}/{plural}/', rowTemplate: '#{name}RowTemplate', rowsContainer: '#{plural}Container', entryTemplate: '#{name}EntryTemplate', entryContainer: '#{name}EntryContainer', entryForm: '#{name}Form', entryIdField: 'idField', selectLinks: '.{name}Select', deleteLinks: '.{name}Löschen', onSelect: $.noop, onReload: $.noop }; [/javascript] Die an den Konstruktor übergebenen Optionen erweitern die Standardoptionen, so dass Sie die Optionen nach Belieben anpassen können. Nachdem die Erweiterung angewendet wurde, werden die Werte erweitert. Dabei wird im Grunde jedes Vorkommen von {...} durch den Optionswert ersetzt, auf den dieser Name verweist. [javascript highlight="10,11,12,13,14,15,16,17,18"] function expand(options) { for (var Feld in Optionen) { var val = options[field]; if ($.type(val) == 'string') { while (val.match(/{([^{}])}/)) { val = val.replace(/{([^{}])}/, function (key, group) { return options[group]; }); } if (val.charAt(0) == '#' || val.charAt(0) == '.') { var obj = $(val); if (val.charAt(0) == '#') { if (obj.length == 0) { obj = undefiniert; } } val = obj; } options[field] = val; } } } [/javascript] Die hervorgehobenen Zeilen in diesem Codefragment lösen alle CSS-Selektoren durch jQuery auf, nachdem sie erweitert wurden. Als Beispiel (und kurze Vorschau) zeigt der Screenshot unten eine Seite mit zwei Master-Detail-Implementierungen, eine für Bücher und eine für Autoren, die beide mit diesem Plugin implementiert wurden.

Screen-shot-2011-09-15-at-4.47.20

Fazit

Das Schreiben eines JavaScript-Clients für RESTful-Webdienste ist mit jQuery und jQuery Template sehr einfach. Den Quellcode für diesen Blog können Sie hier herunterladen: books.html jquery.rest-client.js books.css Da das Backend nicht eingerichtet ist, ist die Seite nicht funktionsfähig ;)

Verfasst von

Maarten Winkels

Contact

Let’s discuss how we can support your journey.