Integration von JavaScript-Bibliotheken von Drittanbietern in sgcHTML | eSeGeCe Blog

Integration von JavaScript-Bibliotheken von Drittanbietern in sgcHTML

· Komponenten

Eine Frage, die wir häufig von Leuten hören, die sgcHTML evaluieren: Es bringt viele fertige Komponenten mit, aber was ist, wenn ich bereits eine JavaScript-Bibliothek habe, die ich mag? Vielleicht bevorzugen Sie Diagramme von D3.js oder Chart.js, oder Sie haben für ein DevExtreme-Grid bezahlt und möchten es einsetzen. Die kurze Antwort lautet: sgcHTML wurde offen konzipiert. Es erzeugt vollständige Seiten auf dem Server aus Delphi-, C++Builder- oder .NET-Code, und jede Bibliothek, die im Browser läuft, lässt sich mit drei kleinen Hooks einbinden. Wenn Sie dieselbe Bibliothek häufig verwenden, können Sie sie in einer wiederverwendbaren Komponente kapseln, sodass der Rest Ihrer App das JavaScript überhaupt nicht mehr zu Gesicht bekommt.

Die drei Hooks

Jedes Drittanbieter-Widget benötigt auf der Seite dieselben drei Dinge: Die Bibliothek muss geladen werden, es muss ein Element geben, in das hinein sie rendert, und eine Zeile Init-Code muss ausgeführt werden, sobald das DOM bereit ist. Die Komponente TsgcHTMLComponent_Site stellt Ihnen dafür jeweils eine Property oder Methode bereit.

Hier ist ein vollständiges D3.js-Beispiel. Laden Sie die Bibliothek, fügen Sie einen Container ein, und lassen Sie D3 hineinzeichnen.

uses
  sgcHTML_Component_Site;

var
  oSite: TsgcHTMLComponent_Site;
begin
  oSite := TsgcHTMLComponent_Site.Create(nil);
  try
    oSite.Title := 'Sales Dashboard';

    // 1) Load the library (a public CDN, or a file you serve yourself)
    oSite.CustomHead := '<script src="https://d3js.org/d3.v7.min.js"></script>';

    // 2) Emit the element your library renders into
    oSite.AddContent('<div id="chart"></div>');

    // 3) Run your init code at the end of the body
    oSite.BodyEndHTML :=
      '<script>' +
      '  const data = [4, 8, 15, 16, 23, 42];' +
      '  d3.select("#chart").selectAll("div").data(data).enter()' +
      '    .append("div").attr("class", "bar")' +
      '    .style("width", d => (d * 12) + "px").text(d => d);' +
      '</script>';

    WebModule.Response := oSite.HTML;
  finally
    oSite.Free;
  end;
end;

Das ist das gesamte Muster. Tauschen Sie die URL und den Init-Code aus, und dieselben drei Zeilen tragen Chart.js, ein DevExtreme-Widget, einen Kalender, eine Karte oder alles andere.

CDN oder selbst gehostet

Das obige Beispiel verweist auf ein öffentliches CDN, was der schnellste Weg ist, etwas auszuprobieren. Für eine Intranet-Anwendung, ein Offline-Deployment oder wenn Sie eine exakte Version festlegen möchten, liefern Sie die Bibliotheksdatei selbst vom sgc-Server aus und referenzieren Sie sie über einen lokalen Pfad. Mehrere mitgelieferte Komponenten tun dies bereits: Die integrierte Chart-Komponente lädt Chart.js von einem lokalen /chart.umd.min.js statt von einem CDN, sodass die Seite keine ausgehende Abhängigkeit hat.

// The .js and .css files sit next to your server executable and are
// served as static files, so the page references them by local path:
oSite.CustomHead :=
  '<link rel="stylesheet" href="/assets/mylib.min.css">' +
  '<script src="/assets/mylib.min.js"></script>';

Wenn Sie den Head lieber als Nodes statt als reinen String aufbauen möchten, stellt die Template-Schicht die Listen HeadNodes und BodyEndNodes mit den Hilfsfunktionen AddScript und AddStylesheet bereit, dazu die Nodes TsgcHTMLScript (mit Src, Code, Defer, Async) und TsgcHTMLStylesheet. Rohe Strings und typisierte Nodes sind austauschbar, verwenden Sie, was in Ihrem Code besser lesbar ist.

Chart.js in wenigen Zeilen

Chart.js wird häufig nachgefragt, daher folgt hier dasselbe über dieselben drei Hooks. Laden Sie die Bibliothek, geben Sie ein <canvas> aus und erstellen Sie dann das Diagramm.

oSite.CustomHead := '<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>';

oSite.AddContent('<canvas id="revenue" width="600" height="300"></canvas>');

oSite.BodyEndHTML :=
  '<script>' +
  '  new Chart(document.getElementById("revenue"), {' +
  '    type: "bar",' +
  '    data: {' +
  '      labels: ["Q1", "Q2", "Q3", "Q4"],' +
  '      datasets: [{ label: "Revenue", data: [12, 19, 7, 15] }]' +
  '    }' +
  '  });' +
  '</script>';

Wenn Sie nur Diagramme möchten und keine bestimmte Bibliothek benötigen: sgcHTML liefert bereits die Komponente TsgcHTMLComponent_Chart, bei der Chart.js für Sie fertig verdrahtet ist, mit einer CustomOptions-Property, die jede rohe Chart.js-Konfiguration direkt durchreicht. Dieser Abschnitt richtet sich an alle, die die Bibliothek selbst steuern möchten.

Ein DevExtreme Grid

DevExtreme ist ein gutes Beispiel für eine umfangreichere kommerzielle Suite. Das DataGrid benötigt sein Stylesheet und sein Bundle geladen, ein Element zum Einhängen und ein Konfigurationsobjekt. Am Vorgehen ändert sich nichts.

oSite.CustomHead :=
  '<link rel="stylesheet" href="https://cdn3.devexpress.com/jslib/23.2.3/css/dx.light.css">' +
  '<script src="https://cdn3.devexpress.com/jslib/23.2.3/js/dx.all.js"></script>';

oSite.AddContent('<div id="grid"></div>');

oSite.BodyEndHTML :=
  '<script>' +
  '  new DevExpress.ui.dxDataGrid(document.getElementById("grid"), {' +
  '    dataSource: [' +
  '      { id: 1, name: "Widget", price: 9.90 },' +
  '      { id: 2, name: "Gadget", price: 14.50 }' +
  '    ],' +
  '    columns: ["id", "name", "price"],' +
  '    showBorders: true' +
  '  });' +
  '</script>';

Der interessante Teil ist, woher dataSource kommt. Sie können einen Datensatz auf dem Server als JSON serialisieren und wie gezeigt inline einbetten, oder das Grid auf einen Endpunkt zeigen lassen, den Ihr sgc-Server bereitstellt, und es live laden lassen. Im Demos-Ordner gibt es ein fertiges DevExtreme-Grid-Beispiel, das ein Grid von einem sgc-WebSocket-Server speist, sodass sich die Daten in Echtzeit aktualisieren, ohne dass die Seite neu geladen werden muss.

In einer wiederverwendbaren Komponente kapseln

Rohe Strings herumzureichen ist für einen Einzelfall völlig in Ordnung. Wenn Sie sich dabei ertappen, dieselbe Bibliothek Seite für Seite zu verwenden, heben Sie sie zu einer Komponente an: Leiten Sie eine Unterklasse von TsgcHTMLComponent ab und überschreiben Sie GetHTML, um Ihr eigenes Markup plus den Bibliotheks-Klebecode zurückzugeben. Überall sonst in Ihrer App setzen Sie dann eine Property und rufen HTML auf, genau wie bei einer eingebauten Komponente.

uses
  sgcHTML_Component;

type
  TsgcHTMLComponent_D3Bars = class(TsgcHTMLComponent)
  private
    FData: string;   // a JSON array, e.g. '[4,8,15,16,23,42]'
  published
    property Data: string read FData write FData;
  public
    function GetHTML: string; override;
  end;

function TsgcHTMLComponent_D3Bars.GetHTML: string;
begin
  Result :=
    '<div id="d3bars"></div>' +
    '<script src="https://d3js.org/d3.v7.min.js"></script>' +
    '<script>' +
    '  const data = ' + FData + ';' +
    '  d3.select("#d3bars").selectAll("span").data(data).enter()' +
    '    .append("span").style("padding-right", d => d + "px").text(d => d);' +
    '</script>';
end;

Das ist kein Sonderfall, sondern genau so sind etliche der mitgelieferten Komponenten gebaut. Die Map-Komponente kapselt Leaflet, der RichEditor kapselt Quill, das KanbanBoard kapselt SortableJS, und der PDFViewer kapselt pdf.js, jede von ihnen lädt ihre Bibliothek und gibt ihren Init-Code genau über diese Hooks aus. Ihr eigener Wrapper reiht sich als vollwertige Komponente daneben ein, und Sie können bei Bedarf einen passenden GetCSS-Override hinzufügen, falls die Bibliothek Styles benötigt, die sgcHTML für Sie über die gesamte Seite hinweg sammelt und dedupliziert.

Probieren Sie es aus

Das Fazit ist: sgcHTML schränkt Sie nie ein. Die eingebauten Komponenten decken viel Boden ab, und wenn Sie etwas Bestimmtes brauchen, D3.js, Chart.js, ein DevExtreme Grid oder Chart, oder jede beliebige Browser-Bibliothek, sind es drei Hooks zum Einbinden und eine kleine Unterklasse, um es wiederverwendbar zu machen.

Fragen oder Feedback? Nehmen Sie Kontakt auf, Sie erhalten eine Antwort von den Leuten, die den Code geschrieben haben.