Una domanda che sentiamo spesso da chi valuta sgcHTML: include già molti componenti pronti all'uso, ma cosa succede se ho già una libreria JavaScript che preferisco? Magari preferisci i grafici di D3.js o Chart.js, oppure hai pagato una DevExtreme Grid e vuoi usarla. La risposta breve è che sgcHTML è stato progettato per essere aperto. Genera pagine complete sul server a partire da codice Delphi, C++Builder o .NET, e qualsiasi libreria che gira nel browser si integra con tre piccoli hook. Se usi spesso la stessa libreria, puoi incapsularla in un componente riutilizzabile, così il resto della tua app non vede mai il JavaScript.
I tre hook
Ogni widget di terze parti richiede le stesse tre cose sulla pagina: la libreria deve essere caricata, deve esistere un elemento in cui effettuare il rendering, e una riga di codice di inizializzazione deve essere eseguita una volta che il DOM è pronto. Il componente TsgcHTMLComponent_Site mette a disposizione una proprietà o un metodo per ciascuno di questi passaggi.
CustomHeadinserisce markup all'interno di<head>, la posizione naturale per uno<script>o un<link>che carica la libreria.AddContentemette l'elemento di destinazione nel corpo della pagina.BodyEndHTMLesegue il tuo codice di inizializzazione appena prima di</body>, dopo che la libreria è stata caricata.
Ecco un esempio completo con D3.js. Carica la libreria, inserisci un contenitore, poi lascia che D3 disegni al suo interno.
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;
Questo è l'intero schema. Cambia l'URL e il codice di inizializzazione, e le stesse tre righe funzionano per Chart.js, un widget DevExtreme, un calendario, una mappa o qualsiasi altra cosa.
CDN o self-hosted
L'esempio sopra punta a un CDN pubblico, il modo più rapido per provare qualcosa. Per un'app intranet, una distribuzione offline, o quando vuoi fissare una versione esatta, servi tu stesso il file della libreria dal server sgc e referenziala con un percorso locale. Diversi componenti integrati fanno già così: il componente Chart incluso carica Chart.js da un file locale /chart.umd.min.js anziché da un CDN, così la pagina non ha alcuna dipendenza esterna.
// 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>';
Se preferisci costruire l'head come nodi invece che come stringa grezza, il livello dei template espone le liste HeadNodes e BodyEndNodes con gli helper AddScript e AddStylesheet, oltre ai nodi TsgcHTMLScript (con Src, Code, Defer, Async) e TsgcHTMLStylesheet. Stringhe grezze e nodi tipizzati sono intercambiabili, usa quello che risulta più leggibile nel tuo codice.
Chart.js in poche righe
Chart.js è una richiesta comune, quindi eccolo tramite gli stessi tre hook. Carica la libreria, emetti un <canvas>, poi costruisci il grafico.
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>';
Se ti servono solo dei grafici e non hai bisogno di una libreria specifica, tieni presente che sgcHTML include già un componente TsgcHTMLComponent_Chart che è Chart.js già collegato per te, con una proprietà CustomOptions che passa direttamente qualsiasi configurazione Chart.js grezza. Questa sezione è per quando vuoi guidare tu stesso la libreria.
Una Grid DevExtreme
DevExtreme è un buon esempio di suite commerciale più pesante. La DataGrid richiede il caricamento del proprio foglio di stile e del proprio bundle, un elemento su cui montarsi e un oggetto di configurazione. L'approccio non cambia.
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 parte interessante è da dove proviene dataSource. Puoi serializzare un dataset in JSON sul server e inserirlo in linea come mostrato, oppure puntare la grid verso un endpoint esposto dal tuo server sgc e lasciare che carichi i dati dal vivo. Nella cartella dei demo è disponibile un esempio pronto di Grid DevExtreme che alimenta una grid da un server WebSocket sgc, così i dati si aggiornano in tempo reale senza ricaricare la pagina.
Incapsulala in un componente riutilizzabile
Passare stringhe grezze in giro va bene per un uso occasionale. Quando ti accorgi di usare la stessa libreria pagina dopo pagina, promuovila a componente: crea una sottoclasse di TsgcHTMLComponent e sovrascrivi GetHTML per restituire il tuo markup più il collante della libreria. Ovunque nel resto della tua app imposti semplicemente una proprietà e chiami HTML, esattamente come con un componente integrato.
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;
Non è un caso speciale, è così che sono costruiti molti dei componenti inclusi. Il componente Map incapsula Leaflet, il RichEditor incapsula Quill, il KanbanBoard incapsula SortableJS e il PDFViewer incapsula pdf.js, ognuno dei quali carica la propria libreria ed emette il proprio codice di inizializzazione esattamente tramite questi hook. Il tuo wrapper si affianca a loro come componente di prima classe, e puoi aggiungere un corrispondente override di GetCSS se la libreria richiede stili, che sgcHTML raccoglie e deduplica in tutta la pagina per te.
Provalo
Il concetto chiave è che sgcHTML non ti chiude mai in una scatola. I componenti integrati coprono molto terreno, e quando ti serve qualcosa di specifico, D3.js, Chart.js, una Grid o un Chart DevExtreme, qualsiasi libreria browser, bastano tre hook per integrarla e una piccola sottoclasse per renderla riutilizzabile.
Domande o feedback? Contattaci, riceverai una risposta da chi ha scritto il codice.
