Intégrer des bibliothèques JavaScript tierces dans sgcHTML | eSeGeCe Blog

Intégrer des bibliothèques JavaScript tierces dans sgcHTML

· Composants

Une question que l'on entend souvent de la part de ceux qui évaluent sgcHTML : la bibliothèque propose beaucoup de composants prêts à l'emploi, mais que faire si vous avez déjà une bibliothèque JavaScript que vous préférez ? Peut-être aimez-vous les graphiques de D3.js ou de Chart.js, ou avez-vous payé pour une DevExtreme Grid que vous souhaitez utiliser. La réponse courte est que sgcHTML a été conçu pour rester ouvert. Il génère des pages complètes côté serveur à partir de code Delphi, C++Builder ou .NET, et n'importe quelle bibliothèque fonctionnant dans un navigateur s'intègre avec trois petits hooks. Si vous utilisez souvent la même bibliothèque, vous pouvez l'encapsuler dans un composant réutilisable afin que le reste de votre application ne voie jamais le JavaScript.

Les trois hooks

Chaque widget tiers a besoin des trois mêmes éléments sur la page : la bibliothèque doit être chargée, il doit exister un élément dans lequel elle puisse s'afficher, et une ligne de code d'initialisation doit s'exécuter une fois le DOM prêt. Le composant TsgcHTMLComponent_Site vous offre une propriété ou une méthode pour chacun de ces besoins.

Voici un exemple complet avec D3.js. Chargez la bibliothèque, ajoutez un conteneur, puis laissez D3 y dessiner.

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;

C'est tout le principe. Changez l'URL et le code d'initialisation, et les trois mêmes lignes prennent en charge Chart.js, un widget DevExtreme, un calendrier, une carte, ou tout autre élément.

CDN ou hébergement local

L'exemple ci-dessus pointe vers un CDN public, ce qui est le moyen le plus rapide pour tester quelque chose. Pour une application intranet, un déploiement hors ligne, ou lorsque vous souhaitez figer une version précise, servez vous-même le fichier de la bibliothèque depuis le serveur sgc et référencez-le via un chemin local. Plusieurs composants intégrés procèdent déjà ainsi : le composant Chart fourni charge Chart.js depuis un fichier local /chart.umd.min.js plutôt que depuis un CDN, de sorte que la page n'a aucune dépendance sortante.

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

Si vous préférez construire l'en-tête sous forme de nœuds plutôt que de chaîne brute, la couche de modèles expose les listes HeadNodes et BodyEndNodes, avec les fonctions utilitaires AddScript et AddStylesheet, ainsi que les nœuds TsgcHTMLScript (avec Src, Code, Defer, Async) et TsgcHTMLStylesheet. Chaînes brutes et nœuds typés sont interchangeables, utilisez celui qui rend votre code le plus lisible.

Chart.js en quelques lignes

Chart.js est une demande fréquente, voici donc comment procéder avec les trois mêmes hooks. Chargez la bibliothèque, insérez un <canvas>, puis construisez le graphique.

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

Si vous souhaitez uniquement des graphiques et n'avez pas besoin d'une bibliothèque spécifique, notez que sgcHTML fournit déjà un composant TsgcHTMLComponent_Chart, une intégration de Chart.js prête à l'emploi, avec une propriété CustomOptions qui transmet directement toute configuration Chart.js brute. Cette section s'adresse à ceux qui souhaitent piloter eux-mêmes la bibliothèque.

Une Grid DevExtreme

DevExtreme est un bon exemple de suite commerciale plus lourde. Le DataGrid a besoin que sa feuille de style et son bundle soient chargés, d'un élément sur lequel se monter, et d'un objet de configuration. L'approche reste exactement la même.

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

La partie intéressante est de savoir d'où vient dataSource. Vous pouvez sérialiser un jeu de données en JSON côté serveur et l'insérer en ligne comme montré ici, ou faire pointer la grid vers un endpoint exposé par votre serveur sgc et la laisser charger les données en direct. Il existe un exemple prêt à l'emploi de Grid DevExtreme dans le dossier des démos, qui alimente une grid depuis un serveur sgc WebSocket, de sorte que les données se mettent à jour en temps réel sans rechargement de page.

L'encapsuler dans un composant réutilisable

Manipuler des chaînes brutes convient très bien pour un usage ponctuel. Mais lorsque vous vous retrouvez à utiliser la même bibliothèque page après page, transformez-la en composant : dérivez une sous-classe de TsgcHTMLComponent et redéfinissez GetHTML pour renvoyer votre propre balisage ainsi que le code de liaison de la bibliothèque. Partout ailleurs dans votre application, il vous suffit alors de définir une propriété et d'appeler HTML, exactement comme pour un composant intégré.

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;

Ce n'est pas un cas particulier, c'est ainsi que sont construits plusieurs des composants fournis. Le composant Map encapsule Leaflet, le RichEditor encapsule Quill, le KanbanBoard encapsule SortableJS, et le PDFViewer encapsule pdf.js, chacun chargeant sa bibliothèque et émettant son code d'initialisation via exactement ces mêmes hooks. Votre propre wrapper se place à leurs côtés en tant que composant à part entière, et vous pouvez ajouter une redéfinition GetCSS correspondante si la bibliothèque nécessite des styles, que sgcHTML collecte et dédoublonne automatiquement sur toute la page.

Essayez-le

Le message à retenir est que sgcHTML ne vous enferme jamais. Les composants intégrés couvrent un large éventail de besoins, et lorsque vous avez besoin de quelque chose de spécifique, D3.js, Chart.js, une Grid ou un Chart DevExtreme, ou n'importe quelle bibliothèque de navigateur, il suffit de trois hooks pour l'intégrer et d'une petite sous-classe pour la rendre réutilisable.

Des questions ou des retours ? Contactez-nous, vous obtiendrez une réponse des personnes qui ont écrit le code.