Integracja bibliotek JavaScript innych firm w sgcHTML | Blog eSeGeCe

Integracja bibliotek JavaScript innych firm w sgcHTML

· Komponenty

Pytanie, które często słyszymy od osób oceniających sgcHTML: biblioteka oferuje wiele gotowych komponentów, ale co, jeśli mam już bibliotekę JavaScript, którą lubię? Może wolisz wykresy z D3.js lub Chart.js, albo zapłaciłeś za Grid DevExtreme i chcesz go użyć. Krótka odpowiedź brzmi: sgcHTML zostało zbudowane jako otwarte rozwiązanie. Generuje kompletne strony po stronie serwera z kodu Delphi, C++Builder lub .NET, a dowolna biblioteka działająca w przeglądarce daje się osadzić za pomocą trzech niewielkich punktów zaczepienia. Jeśli sięgasz po tę samą bibliotekę często, możesz opakować ją w komponent wielokrotnego użytku, dzięki czemu reszta aplikacji nigdy nie zobaczy tego kodu JavaScript.

Trzy punkty zaczepienia

Każdy widget innej firmy potrzebuje na stronie tych samych trzech rzeczy: biblioteka musi zostać załadowana, musi istnieć element, do którego zostanie wyrenderowana, oraz musi zostać uruchomiona linia kodu inicjującego, gdy tylko DOM będzie gotowy. Komponent TsgcHTMLComponent_Site udostępnia jedną właściwość lub metodę dla każdego z tych kroków.

Oto kompletny przykład z D3.js. Załaduj bibliotekę, dodaj kontener, a następnie pozwól D3 narysować w nim wykres.

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;

To jest cały wzorzec. Zamień adres URL i kod inicjujący, a te same trzy linie obsłużą Chart.js, widget DevExtreme, kalendarz, mapę lub cokolwiek innego.

CDN czy hosting własny

Powyższy przykład wskazuje na publiczne CDN, co jest najszybszym sposobem, aby coś wypróbować. W przypadku aplikacji intranetowej, wdrożenia offline lub gdy chcesz przypiąć dokładną wersję, hostuj plik biblioteki samodzielnie na serwerze sgc i odwołuj się do niego lokalną ścieżką. Kilka wbudowanych komponentów już tak robi: dołączony komponent Chart ładuje Chart.js z lokalnego pliku /chart.umd.min.js zamiast z CDN, dzięki czemu strona nie ma żadnej zależności zewnętrznej.

// 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>';

Jeśli wolisz budować sekcję head jako węzły zamiast surowego ciągu znaków, warstwa szablonów udostępnia listy HeadNodes i BodyEndNodes z pomocniczymi metodami AddScript i AddStylesheet, a także węzły TsgcHTMLScript (z Src, Code, Defer, Async) oraz TsgcHTMLStylesheet. Surowe ciągi znaków i typowane węzły są wymienne, użyj tego, co lepiej pasuje do Twojego kodu.

Chart.js w kilku liniach

Chart.js to częsta prośba, więc oto ono za pomocą tych samych trzech punktów zaczepienia. Załaduj bibliotekę, wyemituj element <canvas>, a następnie zbuduj wykres.

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>';

Jeśli zależy Ci wyłącznie na wykresach i nie potrzebujesz konkretnej biblioteki, zwróć uwagę, że sgcHTML zawiera już komponent TsgcHTMLComponent_Chart, który jest gotowym opakowaniem Chart.js z właściwością CustomOptions, przekazującą bezpośrednio dowolną surową konfigurację Chart.js. Ta sekcja jest dla sytuacji, gdy chcesz sterować biblioteką samodzielnie.

Grid DevExtreme

DevExtreme to dobry przykład cięższego pakietu komercyjnego. DataGrid potrzebuje załadowanego arkusza stylów i pakietu, elementu do zamontowania oraz obiektu konfiguracyjnego. W podejściu nic się nie zmienia.

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>';

Interesującą częścią jest to, skąd pochodzi dataSource. Możesz zserializować zbiór danych do JSON po stronie serwera i osadzić go inline, jak pokazano powyżej, albo skierować grid na punkt końcowy udostępniany przez Twój serwer sgc i pozwolić mu ładować dane na żywo. W folderze demo znajduje się gotowy przykład Grid DevExtreme, który zasila grid z serwera WebSocket sgc, dzięki czemu dane aktualizują się w czasie rzeczywistym bez przeładowania strony.

Opakuj to w komponent wielokrotnego użytku

Przekazywanie surowych ciągów znaków sprawdza się dla jednorazowego zastosowania. Gdy zauważysz, że używasz tej samej biblioteki na stronie po stronie, przekształć ją w komponent: podklasuj TsgcHTMLComponent i przesłoń GetHTML, aby zwracać własny znacznik wraz z kodem łączącym bibliotekę. W pozostałej części aplikacji wystarczy wtedy ustawić właściwość i wywołać HTML, dokładnie tak jak w przypadku wbudowanego komponentu.

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;

To nie jest przypadek specjalny, tak właśnie zbudowana jest część dostarczanych komponentów. Komponent Map opakowuje Leaflet, RichEditor opakowuje Quill, KanbanBoard opakowuje SortableJS, a PDFViewer opakowuje pdf.js, każdy z nich ładuje swoją bibliotekę i emituje kod inicjujący dokładnie za pomocą tych samych punktów zaczepienia. Twoje własne opakowanie staje obok nich jako pełnoprawny komponent, a jeśli biblioteka wymaga stylów, możesz dodać odpowiadające mu przesłonięcie GetCSS, które sgcHTML zbiera i deduplikuje na całej stronie za Ciebie.

Wypróbuj

Wniosek jest taki, że sgcHTML nigdy Cię nie ogranicza. Wbudowane komponenty pokrywają wiele przypadków, a gdy potrzebujesz czegoś konkretnego, D3.js, Chart.js, Grid lub Chart DevExtreme, dowolną bibliotekę przeglądarkową, wystarczą trzy punkty zaczepienia, aby ją osadzić, i jedna niewielka podklasa, aby uczynić ją wielokrotnego użytku.

Masz pytania lub uwagi? Skontaktuj się z nami, otrzymasz odpowiedź od osób, które napisały ten kod.