Blog

RAD Studio WebStencils: Die Delphi-Alternative zu Next.js & Co.

Warum WebStencils die Entwicklung von Webanwendungen revolutionieren kann

May 9, 2025

RAD Studio WebStencils bringt Server-Side Rendering (SSR) zurück ins Zentrum moderner Webentwicklung – effizient, flexibel und nahtlos in Delphi integriert. Mit Unterstützung für HTMX, HTML- und CSS-Templates sowie einfacher Datenbankanbindung lassen sich Webanwendungen entwickeln, ganz ohne komplexe JavaScript-Frameworks. Entdecken Sie, wie WebStencils Webentwicklung mit Delphi neu definiert.

RAD Studio WebStencils: Die Renaissance des Server-Side Renderings

In einer Zeit, in der sich Web-Entwicklungstrends ständig wandeln, erleben wir eine bemerkenswerte Rückkehr zu den Wurzeln: Server-Side Rendering (SSR) erlebt durch moderne Werkzeuge wie RAD Studio WebStencils eine Renaissance. In diesem Artikel werden Sie genau diese Technologie grundlegend kennenlernen, die Ihnen helfen wird, Ihre Webanwendungen schneller und effizienter zu entwickeln. Bevor wir in die Welt der WebStencils eintauchen, werfen wir einen Blick auf die Evolution der Web-Entwicklung und die Herausforderungen, die mit modernen Ansätzen verbunden sind. Anschließend werden die Grundlagen von WebStencils anhand

von einfachen Desktop-Anwendungen vermittelt – von der Komplexität einer Webanwendung sind wir hier noch weit entfernt. Abschließend werden wir eine simple Webseite mit Inhalten aus einer Datenbank mit WebStencils erstellen, um die Möglichkeiten dieser neuen Technologie zu demonstrieren.

 

Die Evolution der Web-Entwicklung

  • Die Web-Entwicklung hat in den letzten Jahren einen interessanten Kreislauf durchlaufen. Während in den 2010er Jahren Client-Side Rendering mit Frameworks wie Angular und React dominierte, zeigen sich heute die Grenzen dieser Architektur:
    Längere First-Paint-Zeiten: Die Zeit, bis der Benutzer etwas sieht, ist oft recht lang.
  • Herausforderungen bei der SEO-Optimierung: Suchmaschinen haben Schwierigkeiten, den Inhalt von SPAs zu indexieren.
  • Hohe JavaScript-Bundle-Größen: Die Größe der JavaScript-Bundles kann die Ladezeiten erheblich beeinflussen. Gerade bei der Nutzung auf mobilen Geräten macht sich das schnell bemerkbar.
  • Komplexe State-Management-Anforderungen: Die Verwaltung des Anwendungsstatus kann schnell unübersichtlich werden.

Ironischerweise gab es in den Anfangsjahren des Webs eine klare Trennung zwischen Server- und Client-Rendering. Mit der Einführung von SPAs verschwammen diese Grenzen zunehmend, was zu einer Vielzahl von Herausforderungen führte. Mit Delphi DataSnap und RAD Server wurden bereits vor vielen Jahren Lösungen für diese Probleme entwickelt. Allerdings waren diese Lösungen nicht in der Lage, die Vorteile von SSR zu nutzen. Insbesondere mit RAD Server wurden andere Ziele verfolgt. Es ging um die Entwicklung von REST-APIs und nicht um die Entwicklung von Webanwendungen. Mit RAD Studio WebStencils wird diese Lücke nun geschlossen. Zudem stellt Embarcadero mit WebStencils auch zahlreiche Beispiele für den Einsatz von HTMX auf der Client Seite bereit, die die Möglichkeiten von WebStencils eindrucksvoll demonstrieren. Ohne JavaScript-Frameworks, ohne Node.js und ohne Build-Prozesse kann so nun eine moderne Webanwendung entwickelt werden. Eine Erweiterung mit JavaScript-Frameworks ist jederzeit möglich. Selbstverständlich steht mit TypeScript auch eine typisierte Sprache im Web zur Verfügng, die immer mehr an Prägnanz gewinnt und ebenso mit WebStencils benutzt werden kann. Auch der Einsatz mit anderen Web Frameworks ist möglich. WebStencils kann z.B. auch mit TMS WEB Core genutzt werden. TMS WEB Core ist insbesondere für Delphi Entwickler interessant, da es die Entwicklung von SPAs mit Object Pascal ermöglicht und kein Erlernen einer neuen Programmiersprache erforderlich ist.

 

RAD Studio WebStencils: Die moderne Antwort mit einer bewährten Programmierumgebung

RAD Studio WebStencils vereint die Vorteile klassischer Server-Side Rendering-Ansätze mit modernen Entwicklungspraktiken. Das Framework ermöglicht:

  • Schnellere initiale Seitenladezeiten: Es wird weniger bis gar kein JavaScript benötigt. Der Server liefert die Seite in Form von HTML und CSS direkt an den Client.
  • Verbesserte Suchmaschinenoptimierung: Suchmaschinen können den Inhalt der Seite problemlos indexieren. Verschiedene Seiten werden mit unterschiedlichen URLs bereitgestellt.
  • Reduzierte Client-Last: Der Client muss weniger JavaScript verarbeiten, was die Leistung auf mobilen Geräten verbessert.
  • Optimierte Entwicklungszyklen: Die Entwicklung wird durch die Verwendung von Delphi und RAD Studio erheblich beschleunigt.
  • Direktes Einbinden von Datenbankabfragen: Datenbankabfragen können auf dem Server direkt durchgeführt werden. Sie können bekannte Datenbankkomponenten und Frameworks in Delphi verwenden. Eine Implementierung einer REST API ist somit nicht erforderlich.
  • Nutzung von CSS- und HTML-Templates: WebStencils wurden im Hinblick auf das Verwenden von CSS- und HTML-Templates entwickelt. Sie können Ihre bestehenden Templates problemlos integrieren.
  • Einfache Integration von REST-APIs: REST-APIs können serverseitig integriert werden, um Daten zu laden und anzuzeigen. Selbstverständlich können Sie auch REST-APIs auf der Client-Seite verwenden.
  • Unterstützung von HTMX: WebStencils wurden von Embarcadero im Hinblick auf die Verwendung mit HTMX konzipiert, um die Interaktivität der Anwendung zu erhöhen. Bei HTMX handelt es sich um eine Bibliothek, die zusätzliche HTML Attribute bereitstellt. Es ist keine Programmierung von JavaScript erforderlich. Trotzdem ermöglicht HTMX es Ihnen z.B., HTML-Elemente dynamisch zu aktualisieren, ohne die gesamte Seite neu laden zu müssen.

Der Trend zur Rückkehr zum Server-Side Rendering, auch “SSR Renaissance” genannt, zeigt sich nicht nur bei WebStencils. Frameworks wie Next.js, Remix und Nuxt.js folgen einem ähnlichen Ansatz. Dies unterstreicht die wachsende Erkenntnis, dass ein hybrider Ansatz aus Server- und Client-Rendering oft die beste Lösung darstellt.

Diese Evolution markiert einen wichtigen Wendepunkt in der Web-Entwicklung, bei dem RAD Studio Web-Stencils eine Schlüsselrolle einnimmt.

 

Und was passiert mit den Single Page Applications und dazugehörigen Frameworks?

WebStencils haben nicht das Ziel verfolgt, Single Page Applications (SPAs) zu ersetzen, sondern um sie zu ergänzen. WebStencils können problemlos in bestehende SPAs integriert werden. Sie können sie verwenden, um bestimmte Teile Ihrer Anwendung serverseitig zu rendern, während andere Teile weiterhin als SPAs funktionieren. Somit können Sie die Vorteile beider Ansätze nutzen
und ihr gewohntes Web Framework, wie z.B. TMS WEB Core, weiterhin verwenden.

In vielen Fällen ist eine hybride Lösung die beste Wahl. WebStencils ermöglicht es Entwicklern, die Vorteile beider Ansätze zu nutzen und maßgeschneiderte Lösungen für ihre spezifischen Anforderungen zu erstellen. Insbesondere in TMS WEB Core führt die Kombination von SSR und SPAs zu einer kürzeren und effizienteren Entwicklungszeit.

 

Loslegen mit WebStencils

RAD Studio WebStencils sind Bestandteil von RAD Studio bzw. Delphi 12 und erfordern keine besondere Installation. Erst kürzlich wurde Version 12.3 von RAD Studio vorgestellt, die auch neue Funktionen für WebStencils mitbringt. Sie können nach der Installation sofort loslegen und die Vorteile von Server-Side Rendering in Ihren Projekten nutzen. Embarcadero stellt leider nur wenige Beispiele zur Verfügung. Hier sei auf die zahlreichen Webinare von Embarcadero verwiesen, die auch nachträglich noch auf YouTube
angeschaut werden können. WebStencils wurden hier in den vergangenen Monaten thematisiert und mit zahlreichen Beispielen demonstriert.

 

Wie funktionieren WebStencils?

Im Kern erzeugt WebStencils Textinhalte basierend auf einer Vorlage. Sie können diese Vorlagen entweder als String definieren oder aus einer Datei laden. Es gibt keine zwingende Verbindung zu Webtechnologien. WebStencils kann in jedem Delphi-Projekt verwendet werden. Laut Marco Cantu, dem Produktmanager von Delphi, ist es von Natur aus kein meinungsstarkes (engl. opinionated) Framework. Dies öffnet es für die Verwendung in jedem Szenario und ermöglicht es auch Drittentwicklern, es in ihren eigenen Frameworks zu verwenden. Embarcadero hat daher alle Komponenten für WebStencils aus Klassen abgeleitet, die leicht mit WebBroker, DataSnap und RAD Server verwendet werden können. Dies impliziert auch den Hauptanwendungsfall für WebStencils: Einfache Erstellung von Webseiten oder anderen web-spezifischen Dateiformaten mit einer modernen Template-Engine. Die folgende Abbildung zeigt die drei Bausteine von WebStencils. Eine Vorlage wird in den Prozessor geladen, der an eine Engine gebunden werden kann. Sowohl für Prozessor (Processor) als auch Engine existieren Komponenten, die man einfach auf Formularen und Datenmodulen verwenden kann. Eine Engine kann dbaei mehrere Prozessoren mit gemeinsamen Einstellungen verwalten, ist jedoch völlig optional und kein zwingender Bestandteil der Prozesskette. Die folgenden Beispiele werden sich daher auf die Verwendung des Prozessors (Processor) beschränken. Jedes Textformat kann generiert werden. Heutzutage sind XML, HTML, JSON und Markdown wahrscheinlich die am häufigsten verwendeten Dateiformate. Wir erkennen nun direkt, dass es keinerlei Bindung an die exklusive Verwendung für Webanwendungen gibt. Es ist somit z.B. auch denkbar WebStencils für die Ertstellung von simplen ‘Serienbriefen’ in VCL Desktopanwendungen zu verwenden.

Der Prozessor erzeugt Inhalte basierend auf einer Vorlage. Eine Vorlage enthält Platzhalter, die
durch Text ersetzt werden können. Dieser Text kann durch Ereignisse, eine TDataSet-Instanz oder
Felder und Eigenschaften eines beliebigen Objekts (abgeleitet von TObject) bereitgestellt werden.
Der Prozessor akzeptiert eine einzelne Objektinstanz sowie ein Modul, das seine Eigenschaften
und Felder mithilfe eines benutzerdefinierten Attributs an den Prozessor veröffentlicht.

Zusätzlich zum Textersatz kann die Vorlage spezielle Schlüsselwörter enthalten. Das sind z.B. Aus-
drücke um simple Berechnungen durchzuführen sowie Bedingungen (@if), ob Teile der Vorlage einbezogen werden sollen oder nicht. Außerdem können Sie über alle Datensätze eines Datensets iterieren und bestimmte Passagen der Vorlage für jeden Datensatz generieren (@foreach).

Wenn Sie eine Vorlage angeben, kann sie spezielle Platzhalter enthalten, um Teile aus anderen Vorlagen zu laden bzw. importieren. Auf diese Weise können Sie leicht ein gemeinsames HTML-Dokument definieren und dann individuell mit dem Inhalt für den jeweiligen zu generierenden Inhalt füllen.

Figure 1: So funktionieren WebStencils

Stellen Sie sich eine Website vor, die in drei Hauptgruppen strukturiert ist: – Kopfzeile (Header) mit Navigationsleiste – Hauptinhalt (Main Content) – Fußzeile (Footer)

Sie können dann eine Vorlagendatei angeben, die die Kopf- und Fußzeile enthält, aber der Hauptinhalt wird jedes Mal ersetzt.

Die folgenden Beispiele führen in die Grundlagen der Vorlagenerstellung ein und zeigen, wie man Vorlagenplatzhalter mit Werten durch Ereignisse, Datasets und Objekte ersetzt. Weiterführende Beispiele verwenden die Edge-Webbrowser-Komponente, um responsive Webinhalte darzustellen. Die Vorlagen enthalten dann HTML, das mit Daten aus einer SQLite-Datenbank gefüllt wird. Eine Erstellung von Web Servern mit Web Broker ist ebenfalls möglich, wird aber in diesem Artikel nicht behandelt.

 

Formale Beschreibung von WebStencils

WebStencils ist ein ‘Textinhalt-Generator’, der für serverseitiges Scripting verwendet werden kann. Im Kontext von mehrschichtigen Anwendungen ermöglicht die Skriptsprache innerhalb einer Vorlage die Erstellung beliebiger Textformate im Backend. Die Tatsache, dass Sie sowohl HTML als auch JSON erstellen können, macht es sehr flexibel, sowohl eine API als auch sogenannte visuelle Serverkomponenten zu implementieren. Sie können die Stärke der Delphi-Datenbank-Frameworks nutzen, um Informationen aus Datenbanken einzubeziehen, aber auch Ihre Datenbanken mit Daten zu aktualisieren, die im Frontend eingegeben wurden. Es ist
jedoch nicht nur für mehrschichtige Szenarien geeignet. WebStencils kann auch in herkömmlichen Delphi Windows Desktop-Anwendungen verwendet werden, um Serienbriefe oder andere dynamische Textdokumente zu erstellen. Für Web-Anwendungsfälle kann es leicht in bestehende Web-Frameworks wie z.B. WebBroker, RAD Server, DataSnap und TMS WEB Core integriert werden, um die Erstellung von HTML-Seiten zu unterstützen, die auf Vorlagen basieren.

 

Hallo WebStencils!

Lassen Sie uns WebStencils mit einem sehr einfachen Beispiel kennenlernen. Es kann als das ‘Hello World’ von WebStencils bezeichnet werden. Ich habe eine Windows VCL-Anwendung erstellt. Das Web wird in diesem Beispiel noch keine Rolle spielen. Zuerst müssen wir verstehen, wie WebStencils funktionieren, und dann können wir dies auf Webtechnologien übertragen.

Das Hauptformular der Anwendung enthält eine einzelne Schaltfläche, um den WebStencils-Prozessor zu starten. Der Prozessor kann in der Werkzeugpalette als TWebStencilsProcessor gefunden werden.

Figure 2: Hauptformular des Beispiels im Formulardesigner.

Offensichtlich sehen wir zur Laufzeit nur die einzelne Schaltfläche. Innerhalb des Schaltflächenereignisses werden wir den Code implementieren, um WebStencils zu verwenden. Wenn Sie auf die Schaltfläche klicken, wird nur ein kurzes Nachrichtenfeld mit dem Text –Hello World– angezeigt.

Figure 3: Hauptformular des Beispiels im Formulardesigner.

Der WebStencils Prozessor benötigt eine Vorlage und mindestens ein Objekt, ein Dataset, ein Modul oder ein Ereignis, um auf die Vorlage angewendet zu werden.

Diese schließen sich nicht gegenseitig aus. Sie können gleichzeitig beliebige Eingabequellen anwenden. Das bedeutet, dass Sie einige Ersetzungen in Ihrer Vorlage sowohl mit einem Ereignis als auch mit einem Dataset bereitstellen können, zum Beispiel.

In diesem Beispiel werden wir ein Ereignis bereitstellen, das Text innerhalb der Vorlage ersetzt.

 

Eine Vorlage definieren

Eine Vorlage ist nichts anderes als reiner Text. Sie kann entweder als TStringList in der Eigenschaft InputLines oder als Dateiname angegeben werden. Die Datei wird dann geladen, bevor der Prozessor aufgefordert wird, die Vorlage zu verarbeiten.

Wenn Sie einen Dateinamen angeben, wird jeder andere Vorlageninhalt ignoriert. Nur der Inhalt der angegebenen Datei wird verwendet. Jeder Text im Inhalt bleibt unberührt und wird in die Ausgabe kopiert, es sei denn, Sie verwenden das @-Symbol. Dieses Symbol zeigt WebStencils an, dass Sie entweder eine zu ersetzende Variable oder ein Schlüsselwort definieren möchten. Schlüsselwörter sind zum Beispiel @if oder @foreach und werden später besprochen.

Konzentrieren wir uns darauf, Text innerhalb der Vorlage zu ersetzen.

Processor.InputLines.Text := ’–@Data.Text–’;

Die obige Zeile setzt die Vorlage innerhalb der Processor-Komponente. Die Eigenschaft InputLines ist vom Typ TStringList. Wie bereits erläutert, ist dies eine Alternative zur Angabe eines Pfads zu einer Datei, die die Vorlage enthält. Wir bemerken, dass die Vorlage das @-Symbol gefolgt von Data.Text verwendet. Data wird als Objekt und Text als Eigenschaft bezeichnet. Formal werden diese beiden vom Prozessor ausgewertet und ergeben einen Wert.

Das Objekt und die Eigenschaft sind beide symbolische lokale Namen und können mithilfe des OnValue-Ereignishandlers aufgelöst werden. Alternativ können wir Objekte, Datensätze und Module unter einem ‘Namen’ registrieren, den wir dann mit @ referenzieren können. Der resultierende Wert ist dann entweder eine Zeichenkette, die im OnValue-Ereignis übereinstimmt, eine Objekteigenschaft oder ein Feld eines Datasets.

Da wir in diesem Beispiel einen einfachen Ereignishandler verwenden möchten und keine Datensätze oder Objekte definiert haben, müssen wir ein OnValue-Ereignis für die Processor Komponente implementieren.

 

Schwierige Verwendung von @

Auf den ersten Blick erscheint alles ziemlich einfach zu sein. Wann immer Sie einen Marker definieren möchten, verwenden Sie das @-Symbol. Es gibt nur ein winziges Problem. Was ist, wenn Ihre Vorlage @-Symbole enthält? Es gibt mehrere Fälle, in denen dies passieren kann:

  • Ihre Vorlage enthält eine E-Mail-Adresse: [email protected]
  • Sie möchten einen X (Twitter)-Feed referenzieren: @hflickster
  • Sie verwenden eine HTML-Vorlage, die CSS-Referenzen mit Versionsnummern enthält:
    <link href=”[email protected]/dist/css/bootstrap.min.css”>
  • CSS-Animationsdefinitionen, die @keyframe verwenden.

Dies sind nur einige Beispiele, die mir in den letzten Monaten begegnet sind. Ich bin sicher, es gibt noch viele mehr.

Die Lösung besteht darin, ein zusätzliches @-Symbol hinzuzufügen. Wenn Sie das @-Symbol tatsächlich Teil Ihrer Vorlage machen möchten, müssen Sie zwei davon in Ihre Vorlage aufnehmen. Das bedeutet, wenn Sie @@ schreiben, wird es nach der Verarbeitung zu @. Angenommen, ich möchte meine E-Mail-Adresse in die Vorlage aufnehmen, würde ich die folgende Vorlagenzeichenkette
erstellen:

Email me at holger@@flixengineering.com !

Dies wird zu:

Email me at [email protected] !

Dies kann sehr mühsam sein, insbesondere wenn Sie Dateien von der Festplatte laden, die für andere Zwecke als die Verwendung mit der WebStencil-Vorlagen-Engine vorbereitet wurden. Stellen Sie sich vor, Sie greifen auf ein Repository von HTML-Vorlagen in Ihrem Unternehmen zu, das auch von anderen HTML-Anwendungen verwendet wird. Sie können diese Vorlagen nicht ändern, ohne es den anderen Anwendungen unmöglich zu machen, sie zu verwenden. Daher müssten Sie diese Vorlagen laden, die Änderungen vornehmen und sie dann an WebStencils übergeben. Obwohl dies möglich ist, ist es eine sehr unbefriedigende Lösung. Stattdessen hat WebStencils das @processing-Schlüsselwort eingeführt: – @processing on: Verarbeite Marker in dieser Vorlage. Dies ist die Standardeinstellung. – @processing off: Verarbeite Marker in dieser Vorlage nicht. Dies ermöglicht es, Vorlagen ‘wie sie sind’ an den Prozessor zu übergeben.

Das @processing-Schlüsselwort betrifft die gesamte Vorlage. Daher ist es ratsam, es in Verbindung mit kleineren Vorlagenschnipseln zu verwenden, die Sie mithilfe von @import in eine größere Vorlage laden.

Auf diese Weise kann jeder Schnipsel individuell Marker verarbeiten oder nicht.

 

Implementierung des Ereignisses

Wenn wir die Prozessor-Komponente auswählen und uns ihre Ereignisse ansehen, finden wir das Ereignis namens OnValue.

procedure TForm1.ProcessorValue(Sender: TObject; const AObjectName, APropName:
string; var AReplaceText: string; var AHandled: Boolean);
begin
if (AObjectName = ’Data’) and (APropName = ’Text’) then
begin
AReplaceText := ’Hello World’;
AHandled := True;
end;
end;

Wann immer der Prozessor auf ein @-Symbol gefolgt von einem Objekt und einem Wert stößt, wird OnValue ausgelöst. Im Parameter AObjectName wird der Name des Objekts sowie der Name des Werts in APropName übergeben. Der Codeausschnitt zeigt, dass wir einfach den Namen des Objekts und den Eigenschaftsnamen vergleichen können, ob sie mit dem übereinstimmen, wonach wir
suchen. In diesem Fall möchten wir Data.Text durch Hello World für das Objekt Data und seine Eigenschaft Text ersetzen. Dafür weisen wir den Ersetzungstext der Variablen AReplaceText zu. Diese wird an den Prozessor zurückgegeben, da sie mit var definiert ist. Gleiches gilt für den Parameter AHandled. Sie müssen ihn auf True setzen, wenn Sie eine Ersetzung vorgenommen haben. Auf diese Weise weiß der Prozessor, ob alle Symbolmarkierungen in der Vorlage ersetzt wurden oder nicht. Wenn nicht alle Markierungen behandelt werden, kann der Prozessor eine Exception werfen.

 

Starten des Prozessors

Um den Prozessor zu starten, lesen Sie den generierten Text mit der Content-Funktion. Es sieht aus wie eine Eigenschaft, ist aber tatsächlich eine Funktion:

TWebStencilsProcessor = class(TCustomContentProducer, IWebStencilsComponent)
public
constructor Create(Owner: TComponent); override;
destructor Destroy; override;
/// <summary> Produziert Inhalt aus der Vorlage, die durch InputFileName angegeben wird.
/// Wenn InputFileName leer ist, wird InputLines verwendet. </summary>
function Content: string; override;
Die vollständige Implementierung des Button.OnClick-Ereignisses ist wie folgt:
procedure TForm1.Button1Click(Sender: TObject);
begin
Processor.InputLines.Text := ‘–@Data.Text–‘;
ShowMessage(Processor.Content);
end;

Weisen Sie zuerst die Vorlage mit einem String zu. Zeigen Sie dann den generierten Inhalt mit ShowMessage an. Es wird das OnValue-Ereignis bei Bedarf implizit abgerufen. In diesem Fall wird es nur einmal ausgeführt, da wir nur einen Marker verwenden.

Es ist nicht notwendig, Leerzeichen zwischen Markern und dem Text, der Teil der Vorlage ist, zu lassen. Ich habe absichtlich führende und nachfolgende — Zeichen zur Vorlage hinzugefügt. Nur die Markerdeklaration wird ersetzt. Denken Sie auch daran, dass jedes Mal, wenn Sie Processer.Content lesen, die Vorlage erneut ausgewertet wird. Das bedeutet, dass Sie dieselbe Komponente für verschiedene Vorlagen oder flexiblere Ereignisimplementierungen wiederverwenden können.

 

Ja, es ist so einfach!

Herzlichen Glückwunsch! Dies sind die Grundlagen der Prozessor-Engine von WebStencils. Sie hätten nicht gedacht, dass es so einfach sein würde, oder?

Auf diese Weise können Sie bereits flexible Textinhalte erstellen, die sich beispielsweise auf berechnete Werte stützen.

Das Ersetzen aller Platzhalter im OnValue-Ereignis könnte für alle möglichen Ersetzungen verwendet werden, ist jedoch nicht sehr effizient. Stellen Sie sich vor, ein Dataset hat Dutzende von Feldern und Sie müssen für jedes Feld mehrere Codezeilen im Ereignishandler hinzufügen.

WebStencils bietet Mittel, um dies viel sauberer und effizienter zu handhaben. Daher zeigt das nächste Beispiel, wie man Datensätze aus einem TDataSetverwendet.

 

Verarbeitung von einzelnen Datensätzen und kompletten Datasets

Im ersten Beispiel haben wir gelernt, wie man einen Platzhalter in einer Vorlage mit dem OnValue-Ereignis ersetzt. Jetzt wird es etwas komplexer, da wir Datasets mit WebStencils einführen. Auch hier verwenden wir eine Windows VCL-Anwendung, um die Funktion zu demonstrieren. Zur Entwurfszeit platzieren wir zwei Schaltflächen auf dem Formular. Eine Schaltfläche wird ein Beispiel ausführen, dass eine Vorlage verwendet, um den aktuellen Datensatz des Datasets zu verarbeiten (Process current record). Die andere Schaltfläche startet ein Beispiel, das eine Vorlage erstellt, um das gesamte Dataset zu durchlaufen (Process ALL records). Der vom Prozessor generierte Text wird im Memo-Steuerelement namens txtResult angezeigt.

Figure 4: Hauptformular des Beispiels im Formulardesigner.

Die Daten werden in einem TFDMemTable gespeichert. Dies ist die FireDAC-Komponente, die ein Client Dataset mit vielen weiteren Funktionen als die traditionelle TCientDataset-Komponente kapselt. Die in diesem Dataset definierten Daten sind trivial. Wenn Sie den Feldeeditor öffnen, sehen Sie, dass es drei Felder mit primitiven Datentypen gibt.

Wir können den Vor- und Nachnamen einer Person sowie ihr Alter speichern. Die ersten beiden Felder sind natürlich vom Typ String, das letzte Feld ist ein Integer-Feld. Eine der großartigen Funktionen von FireDAC ist, dass Sie die Daten dieser Datensätze zur Entwurfszeit im Formular-Editor bearbeiten können. Klicken Sie einfach mit der rechten Maustaste auf die TFDMemTable-Komponente und wählen Sie Edit DataSet… aus, um den Entwurfszeit-Dateneditor zu öffnen.

Figure 5: Feld-Editor des FDMemTable

Figure 6: Daten können direkt zur Entwurfszeit bearbeitet werden.

Figure 7: Der Entwurfszeit-Dateneditor von FireDAC

Die Daten werden innerhalb des Formulars gespeichert und sind zur Laufzeit verfügbar. Dies macht das Erstellen von Demos mit FireDAC zu einer Freude, da Sie für einfache Beispiele wie dieses keine Datenbankdatei benötigen. Wenn Sie die Anwendung ausführen, können Sie entweder den aktuellen Datensatz (der der erste sein wird) oder alle Datensätze verarbeiten. Wenn Sie möchten, können Sie dieses Demo erweitern, indem Sie ein Navigator-Steuerelement hinzufügen, um durch das Datenset zu navigieren.

 

Zugriff auf den aktuellen Datensatz

Um auf einen Datensatz zuzugreifen, können Sie die Methode verwenden, die Sie im ersten Beispiel gelernt haben. Sie binden das Dataset an einen Objektnamen, und die Feldnamen werden als Werte verwendet. Schauen wir uns die verwendete Vorlage an:

procedure TForm1.btnProcessOneClick(Sender: TObject);
begin
Processor.InputLines.Text :=
”’
Text is @People.LastName!
Text is @People.FirstName @(UpperCase(People.LastName))
”’;
Process;
end;

Erneut weisen wir InputLines.Text einen String zu. Die erste Zeile der Vorlage bezieht sich auf People.LastName. Der Wert von People bezieht sich auf das Feld LastName. Wie können wir WebStencils mitteilen, People an das Dataset zu binden? Die Prozessorkomponente Processor hat eine Methode namens AddVar, die drei Parameter akzeptiert:

  1. Einen Namen für das zu bindende Objekt.
  2. Eine Objektreferenz auf ein Dataset.
  3. Einen Boolean-Wert, ob diese Objektreferenz nach der Verarbeitung aus dem Speicher freigegeben werden soll.

Im Code sieht das so aus:

Processor.AddVar( ’People’, People, False );

Das bedeutet, wir binden das Dataset People an den Marker namens People. Beachten Sie, dass der Markername ein String ist. Der letzte Parameter ist False, da die VCL People freigibt, wenn die Anwendung beendet wird. Wenn Sie True angeben würden, würde das Dataset nach der Verarbeitung freigegeben, und Sie würden höchstwahrscheinlich eine Zugriffsverletzung erhalten, wenn die Anwendung beendet wird. Wenn Sie den Prozessor ein zweites Mal ausführen, wird er mit Sicherheit eine Zugriffsverletzung auslösen, da das Dataset nicht mehr verfügbar ist.

Zusammenfassend weist die Zeile das Dataset People allen @People-Vorkommen zu. Dies impliziert auch, dass jeder Wert ein Feld im Dataset sein muss. Betrachtet man @People.LastName, ist dies korrekt, und wir erwarten, dass der Nachname anstelle des Markers eingefügt wird, wenn er verarbeitet wird.

Die nächste Zeile der Vorlage fügt ebenfalls den Vornamen mit @People.FirstName ein. Anschließend wird die nächste Ersetzung etwas komplexer, zeigt jedoch, dass wir sogar Funktionen aus Delphi in unserer Vorlage verwenden können. WebStencils ist ziemlich intelligent und löst Funktionen mithilfe von RTTI auf.

Falls Sie nicht wissen, was RTTI ist, denken Sie einfach so darüber: Sie können Funktionsnamen eingeben, die für die Prozessorkomponente zugänglich sind, und dieser wird in der Lage sein, diese Funktionen aufzurufen.

Wenn Sie einen Ausdruck auswerten möchten, müssen Sie ihn in Klammern setzen. Anstelle von @People.LastName müssen Sie also @( Ausdruck ) verwenden. Die Klammern sind der Schlüssel, damit es funktioniert. So wird @( UpperCase( @People.LastName )) zuerst @People.LastName auswerten und das Ergebnis dann in UpperCase einfügen. Beachten Sie jedoch, dass Sie keine String-Helfer oder andere Dinge mit Duck Typing aufrufen können. Dies wird nicht funktionieren:

@People.LastName.toUpper

Das würde buchstäblich zu folgendem Ergebnis führen, wenn der Nachname des Datensatzes auf Reacher gesetzt ist:

Reacher.toUpper

Der .toUpper-Teil wird einfach zu Text und nicht zu einem Ausdruck. Außerdem ist es ratsam, Ausdrücke sehr kurz zu halten. Verwenden Sie berechnete Felder, wann immer Sie Datensätze mit WebStencils verwenden. Ihr Code wird dadurch viel sauberer, und Sie haben Zugriff auf die gesamte Vielfalt der Object Pascal-Sprache. Genau wie im ersten Beispiel ist dies bereits alles, was Sie tun müssen, um einen einzelnen Datensatz zu verarbeiten:

  1. Legen Sie die Vorlage fest.
  2. Verknüpfen Sie Marker mit einem Dataset.
  3. Führen Sie den Prozessor aus.
  4. Geben Sie das Ergebnis aus.

Beachten Sie, dass Sie den zweiten Schritt nur einmal implementieren müssen und die Vorlage so oft ändern können, wie Sie möchten. Die Referenz des Namens zum Dataset bleibt erhalten. Wir nutzen dies im Beispiel, da ich das Symbolmarker nur einmal für beide Anwendungsfälle binde, nachdem das Formular erstellt wurde:

procedure TForm1.FormCreate(Sender: TObject);
begin
txtResult.Clear;
Processor.AddVar( ‘People’, People, False );
end;

Wie zuvor gezeigt, weisen wir im Button-Klick-Ereignis die Vorlage zu und führen den Prozessor aus, indem wir die Methode Process aufrufen. Die Methode Process weist den Inhalt der Memo-Komponente zu. Implizit wird dadurch die Vorlage verarbeitet.

procedure TForm1.Process;
begin
txtResult.Lines.Text := Processor.Content;
end;

Figure 8: Ausgabe des Beispiels für einen Datensatz

 

Iterieren aller Datensätze

Wenn wir alle Datensätze iterieren und eine Vorlage darauf anwenden möchten, müssen wir die Vorlage ändern und nicht wie man vielleicht denken könnte die Art und Weise, wie wir den Prozessor aufrufen. Genauer gesagt, kommt hier das Schlüsselwort @foreach ins Spiel. Wir werden zunächst die vereinfachte Variante des Schlüsselworts verwenden, bevor wir uns die formal korrekte und bessere Methode ansehen, eine Iteration in der WebStencils-Skriptsprache auszudrücken. Das Schlüsselwort @foreach funktioniert ähnlich wie Delphis relativ neue for-in-Schleife. Denken Sie an die Delphi-Schleife for var person in people. Wenn people eine Liste ist, iterieren wir die Liste, und jedes Element der Liste wird in person zurückgegeben. Genau so funktioniert @foreach. Der Name leitet sich tatsächlich von anderen Programmiersprachen ab, die keine for-in-Schleife, sondern ein foreach-Schlüsselwort haben. Schauen wir uns die Vorlage an, die wir verwenden können, um alle Datensätze in einem Dataset zu iterieren. Wie zuvor ist das Dataset an den Marker namens People gebunden.

procedure TForm1.btnProcessAllClick(Sender: TObject);
begin
Processor.InputLines.Text := ”’
@ForEach People {
Text is @loop.LastName!
Text is @loop.FirstName @loop.LastName
}
”’;
Process;
end;

Das Schlüsselwort @foreach wird gefolgt vom Namen des Markers, der auf das Dataset verweist. In diesem Fall verwenden wir erneut People. Dann müssen wir den Teil der Vorlage, der für jeden Datensatz eingefügt werden soll, mit {…} umschließen. Betrachtet man das Beispiel, bedeutet das, dass der folgende Teil für jeden Datensatz eingefügt wird:

Text is @loop.LastName!
Text is @loop.FirstName @loop.LastName

Die Referenz wechselt von People zu @loop. Sie bezieht sich auf die aktuelle Instanz der Iteration. Wie zuvor können wir nun die Feldnamen als Werte verwenden. Somit bezieht sich @loop.LastName auf das Feld LastName des aktuellen Datensatzes des Iterationsschritts. WebStencils stellt sicher, dass es mit dem ersten Datensatz loslegt und mit dem letzten endet. Sie müssen nicht First aufrufen, um den Datensatzzeiger auf den ersten Datensatz zu setzen. WebStencils wird den Datensatzzeiger vor der Verarbeitung auf den ersten Datensatz setzen.

Bei Anwendung der Vorlage auf alle Datensätze wird der Text in der Memo-Komponente wie folgt angezeigt:

 

Erweiterte Nutzung von @ForEach

Das letzte Beispiel verwendet @ForEach, um über alle Datensätze eines Datasets zu iterieren. Jedes Element der Iteration kann mit @loop angesprochen werden. Dies ermöglicht es uns, über eine Liste zu iterieren. Sobald jedoch verschachtelte Schleifen benötigt werden, ist es nicht mehr möglich, auf das aktuelle Element einer der Schleifen zuzugreifen, weil beide Schleifen dann über @loop referenziert werden. Das bedeutet, dass Sie nicht mehr wissen, auf welches Element Sie sich beziehen. In diesem Fall müssen Sie die vollständige Definition von @ForEach verwenden.

Genauer gesagt ist @ForEach Object {} eine vereinfachte Syntax, die von WebStencils aus Komfortgründen bereitgestellt wird. Die vollständige Definition von @ForEach sieht etwas komplexer aus:

@ForEach ( var Object in List ) { … }
Das bedeutet, dass unser letztes Beispiel auch wie folgt ausgedrückt werden könnte:

Figure 9: Ausgabe des Beispiels für alle Datensätze

@ForEach ( var loop in People ) { … }
Angenommen, jeder Datensatz in People hätte mehrere Adressen. Sie könnten wie folgt iterieren:
@ForEach ( var person in People ) {
@ForEach ( var address in @person.addresses ) {
Text ist @address.street
Text ist @address.city
}
}

Wir schliessen diesen Artikel damit ab, uns anzuschauen, wie wir HTML-Inhalte mit WebStencils generieren können. Wir verwenden dazu die Edge-Browser-Komponente, um die HTML-Inhalte darzustellen. Die Edge-Browser-Komponente ist Bestandteil von RAD Studio 12.3 und kann in der Werkzeugpalette gefunden werden. Sie ist eine Wrapper-Komponente um den Microsoft Edge WebView2 Browser. Diese Komponente ist die einzige Möglichkeit, HTML-Inhalte in Delphi darzustellen, die auf dem neuesten Stand der Technik sind. Sie können sie verwenden, um HTML-Inhalte darzustellen, die mit WebStencils generiert wurden.

 

Generierung von HTML-Inhalten

Bisher enthielten alle Beispiele nur einfachen Text. Jetzt sind wir bereit, HTML-Tags zur Vorlage hinzuzufügen. Wie Sie sehen werden, ist daran nichts Besonderes. Wir fügen einfach die HTML-Tags zur Vorlage hinzu, und der Inhalt wird wie zuvor generiert. Auch wenn ich kein Tutorial zu HTML geben kann, denken Sie daran, dass HTML die Struktur eines Dokuments und dessen Inhalt bestimmt. Die Formatierung erfolgt in der Regel mit CSS. CSS ist eine Stylesheet-Sprache, die das Layout und die Formatierung von HTML-Dokumenten definiert. CSS wird in der Regel in einem separaten Stylesheet gespeichert, das dann in das HTML-Dokument eingebunden wird. Es ist jedoch auch möglich, CSS direkt im HTML-Dokument zu definieren. Falls Sie sich fragen, warum HTML-Dokumente auch ohne CSS in einem Webbrowser gerendert werden, liegt die Antwort in Ihrem Webbrowser. Jeder Browser hat einen Standardsatz von Stilen (engl. styles), die verwendet werden, wenn Ihr HTML-Dokument keine benutzerdefinierten Stile definiert.

Im Jahr 2024 sehen die Standardvorgaben, die formal als User Agent Style Sheets bezeichnet werden, immer noch ziemlich so aus wie im Jahr 2000. Um eine modern aussehende Webseite zu erstellen, sollten Sie daher immer CSS verwenden. Entweder nutzen Sie ein CSS-Framework wie Bootstrap oder Tailwind, oder Sie definieren Ihre eigenen Styles. In der Regel ist es eine gute Idee, mit einem CSS-Framework zu beginnen, anstatt zu versuchen, Ihre eigenen Styles von Grund auf zu definieren.

Dieses Beispiel verwendet dasselbe Dataset wie das letzte. Ich habe den Button und die Funktionalität entfernt, um nur Inhalte für den aktuellen Datensatz zu generieren. Der Prozessor wird immer das gesamte Dataset durchlaufen. Zu Beginn verwenden wir weiterhin eine Memo-Komponente, um den generierten HTML-Inhalt anzuzeigen. Das Hauptformular sieht jetzt so aus:

Figure 10: Hauptformular des Beispiels im Formulardesigner.

Um HTML zu generieren, müssen wir HTML-Elemente zur Vorlage hinzufügen. Beachten Sie, dass dies ein sehr einfaches Beispiel ist. Es enthält nicht einmal einen Datei-Header, der für eine vollständige Seite erforderlich ist. Der Edge Webbrowser (und auch Chrome) wird die Seite trotzdem rendern. Das Beispiel zeigt nur, dass Sie HTML-Elemente zur Vorlage hinzufügen können.

Schauen wir uns den Quellcode mit der Vorlage an, die diese Ausgabe generiert:

procedure TForm1.btnProcessAllClick(Sender: TObject);
begin
Processor.InputLines.Text := ”’
@ForEach People {
<h1>@loop.FirstName @loop.LastName</h1>
<p>@loop.FirstName is @loop.Age years old.</p>
}
”’;
Process;
end;

Da wir über alle Datensätze iterieren und beabsichtigen, für jeden Datensatz das gleiche HTML-Snippet zu generieren, muss dieser Teil in @ForEach {} eingeschlossen werden. Dann drucken wir den Vor- und Nachnamen als Überschrift erster Ordnung mit <H1>. Die nächste Zeile wird als Absatz formatiert, indem der Text mit <P> eingeschlossen wird. Beachten Sie, dass die Komponente TMemo keine Funktionalität bietet, um das erstellte HTML-Snippet zu interpretieren. Es wird es als einfachen Text anzeigen.

Figure 11: Ergebnis der HTML-Vorlage in der TMemo-Komponente

 

Verwendung der Edge-Browser-Komponente

Im letzten Beispiel haben wir HTML-Inhalte erstellt, die als reiner Text angezeigt wurden. Kein Endbenutzer wird eine solche Ausgabe nutzen können. Wir benötigen eine Komponente, die HTML interpretiert und im Benutzerinterface visualisiert. In modernem Delphi können wir TEdgeBrowser verwenden, der den Chromium-Webbrowser nutzt, der in Microsoft Windows integriert ist.

Wenn Sie Kunde von TMS Software sind, erinnern Sie sich vielleicht daran, dass TMS eine eigene Implementierung für Delphi deutlich früher vorgestellt hat und Sie diese Komponente auch in älteren Delphi-Versionen verwenden können. Darüber hinaus bietet deren Steuerung, die TTMSFNCEdgeWebBrowser genannt wird, mehr Funktionen, um mit den im Browser angezeigten Inhalten zu interagieren. Ihre Brückenfunktionalität, um Daten zwischen Ihrer Anwendung und dem Browser auszutauschen, ermöglicht es Ihnen, Webtechnologie einfach in VCL-Anwendungen zu integrieren. Im Formulardesigner ist nur eine Änderung im Vergleich zum vorherigen Beispiel vorzunehmen. Wir müssen die TMemo-Komponente durch TEdgeBrowser ersetzen.

Figure 12: Hauptformular des Beispiels im Formulardesigner.

Das Template sowie der Prozessor können unverändert bleiben. Es sind keine Änderungen erforderlich.

Die einzigen Änderungen, die wir vornehmen müssen, sind:

  1. Initialisieren Sie die Browser-Komponente. Dies kann im FormCreate-Ereignis erfolgen:
    procedure TForm1.FormCreate(Sender: TObject);
    begin
    Processor.AddVar( ’People’, People, False );
    Browser.CreateWebView;
    end;
    Bevor Sie Inhalte in die Webansicht laden können, müssen Sie diese erstellen. Daher muss die Methode CreateWebView aufgerufen werden.
  2. Um das generierte HTML-Snippet in den Browser zu laden, müssen wir NavigateToString aufrufen:
    procedure TForm1.Process;
    begin
    Browser.NavigateToString( Processor.Content );
    end;

Das war’s schon!

Mit diesen Änderungen und Ergänzungen ist die Anwendung in der Lage, das HTML-Snippet in der Browser-Komponente anzuzeigen. Wie bereits erwähnt, sieht das gerenderte Ergebnis im Standardlayout nach heutigen Maßstäben recht unattraktiv aus.

Figure 13: Ergebnis der HTML-Vorlage in der TEdgeBrowser-Komponente

ALLE NEWS ZUR EKON!