sgcHTML si è appena arricchito di 25 nuovi componenti, e questo è il primo di cinque articoli che li presentano per tema. Iniziamo con quattro componenti costruiti attorno a dati strutturati e in continuo cambiamento: TreeGrid per righe gerarchiche, PivotTable per aggregazioni raggruppate, ActivityFeed per un flusso di eventi in tempo reale, e Splitter per il layout a due pannelli ridimensionabile che così spesso deve contenere tutto quanto sopra. Ogni componente qui sotto è una semplice classe Delphi/C++Builder/.NET: imposti le sue proprietà, chiami HTML, ottieni in cambio markup Bootstrap 5. Nessun framework JavaScript, nessuno step di build.
TreeGrid — tabelle gerarchiche comprimibili
TsgcHTMLComponent_TreeGrid genera una tabella Bootstrap le cui righe si comprimono ed espandono, costruita a partire da semplici coppie di nodi Id / ParentId — la stessa struttura che probabilmente usi già per organigrammi, alberi di categorie o distinte base annidate. Dichiara un TsgcHTMLTreeGridColumn per ogni campo visibile, poi aggiungi i nodi; la relazione padre/figlio disegna l'albero e le frecce di espansione per te.
uses
sgcHTML_Component_TreeGrid;
var
oTreeGrid: TsgcHTMLComponent_TreeGrid;
oColumn: TsgcHTMLTreeGridColumn;
begin
oTreeGrid := TsgcHTMLComponent_TreeGrid.Create(nil);
try
oTreeGrid.TreeGridID := 'orgchart';
oTreeGrid.Sortable := True;
oColumn := oTreeGrid.Columns.Add;
oColumn.Caption := 'Department';
oColumn.FieldName := 'Name';
oColumn := oTreeGrid.Columns.Add;
oColumn.Caption := 'Headcount';
oColumn.FieldName := 'Headcount';
oColumn.Align := tgaRight;
oTreeGrid.AddNode('eng', '', ['Engineering', '42']);
oTreeGrid.AddNode('eng-be', 'eng', ['Backend', '18']);
oTreeGrid.AddNode('eng-fe', 'eng', ['Frontend', '15']);
WebModule.Response := oTreeGrid.HTML; // hierarchical table + scoped CSS + toggle script
finally
oTreeGrid.Free;
end;
end;
// Or bind it straight to a self-referencing dataset (Id / ParentId columns):
oTreeGrid.LoadFromDataSet(qryDepartments, 'Id', 'ParentId');
Se i tuoi dati risiedono già in una tabella auto-referenziante, puoi saltare del tutto le chiamate manuali ai nodi e passare a LoadFromDataSet i nomi dei campi padre e figlio — scorrerà il dataset e costruirà l'intero albero in un solo passaggio.
PivotTable — raggruppa, aggrega, totalizza
TsgcHTMLComponent_PivotTable prende righe piatte e produce una tabella raggruppata e aggregata con totali di riga, totali di colonna e un totale generale, il report che altrimenti costruiresti a mano in un foglio di calcolo. Dichiari RowFields e ColumnFields per il raggruppamento, una o più Measures da aggregare (somma, media, conteggio, minimo, massimo), e un elenco DataFields che indica quali colonne di origine leggere effettivamente.
uses
sgcHTML_Component_PivotTable;
var
oPivot: TsgcHTMLComponent_PivotTable;
oMeasure: TsgcHTMLPivotMeasure;
begin
oPivot := TsgcHTMLComponent_PivotTable.Create(nil);
try
oPivot.Caption := 'Revenue by Region and Quarter';
oPivot.RowFields.Add.FieldName := 'Region';
oPivot.ColumnFields.Add.FieldName := 'Quarter';
oMeasure := oPivot.Measures.Add;
oMeasure.SourceField := 'Amount';
oMeasure.Caption := 'Revenue';
oMeasure.Aggregation := paSum;
oMeasure.Format := '#,##0.00';
oPivot.DataFields.Add('Region');
oPivot.DataFields.Add('Quarter');
oPivot.DataFields.Add('Amount');
oPivot.AddRow(['EMEA', 'Q1', '12000']);
oPivot.AddRow(['EMEA', 'Q2', '15500']);
oPivot.AddRow(['APAC', 'Q1', '9800']);
WebModule.Response := oPivot.HTML; // aggregated table + row/column/grand totals
finally
oPivot.Free;
end;
end;
// Or bind it straight to a dataset (reads only RowFields/ColumnFields/Measures fields):
oPivot.LoadFromDataSet(qrySales);
Tutto ciò che viene dopo AddRow (o LoadFromDataSet) viene calcolato per te: il componente raggruppa le righe grezze, applica l'aggregazione scelta per ogni cella, e aggiunge automaticamente le righe e colonne di totale riga/colonna/generale.
ActivityFeed — un flusso di eventi live e trasmissibile
TsgcHTMLComponent_ActivityFeed genera un elenco, dal più recente al meno recente, di voci "chi ha fatto cosa" con timestamp relativi ("3 min fa", mantenuti aggiornati lato client), ed è pensato fin dall'inizio per ricevere aggiornamenti push via WebSocket, non solo per essere renderizzato una volta sola. MaxItems lo mantiene un buffer circolare — la voce più vecchia viene eliminata automaticamente una volta raggiunto il limite.
uses
sgcHTML_Enums, sgcHTML_Component_ActivityFeed;
var
oFeed: TsgcHTMLComponent_ActivityFeed;
begin
oFeed := TsgcHTMLComponent_ActivityFeed.Create(nil);
try
oFeed.FeedID := 'timeline';
oFeed.Title := 'Recent Activity';
oFeed.MaxItems := 30;
oFeed.AddActivity('Ana', 'closed', 'ticket #482', hcSuccess, 'bi bi-check-circle');
oFeed.AddActivity('Marc', 'commented on', 'invoice #1190', hcPrimary);
WebModule.Response := oFeed.HTML; // feed list + scoped CSS + live relative-time script
finally
oFeed.Free;
end;
end;
// Push a new item live to connected clients (out-of-band htmx fragment):
oFeed.AddActivity('Ana', 'approved', 'purchase order #77', hcSuccess);
Engine.BroadcastFragment(oFeed.GetLastItemFragmentHTML);
Quest'ultima riga è la parte interessante: GetLastItemFragmentHTML renderizza solo la voce più recente come frammento htmx out-of-band, e BroadcastFragment lo invia a ogni browser aperto tramite il server sgcWebSockets che stai già eseguendo. Nessun polling, nessun livello real-time separato da configurare.
Splitter — il contenitore a due pannelli che racchiude tutto
TsgcHTMLComponent_Splitter è il pezzo di layout: due pannelli ridimensionabili con una barra di trascinamento tra loro, orizzontale o verticale, con una dimensione minima in pixel su ciascun lato in modo che nessun pannello possa essere ridotto a zero. È esattamente il contenitore di cui ha bisogno una schermata TreeGrid-più-pannello-di-dettaglio o elenco-file-più-editor.
uses
sgcHTML_Component_Splitter;
var
oSplitter: TsgcHTMLComponent_Splitter;
begin
oSplitter := TsgcHTMLComponent_Splitter.Create(nil);
try
oSplitter.SplitterID := 'workspace';
oSplitter.Orientation := soHorizontal;
oSplitter.InitialSplit := 30;
oSplitter.MinSizeA := 180;
oSplitter.MinSizeB := 320;
oSplitter.GutterSize := 8;
oSplitter.Height := '560px';
oSplitter.PersistKey := 'workspace-split';
oSplitter.AddPaneA('<div class="p-3"><h5>Files</h5>...</div>');
oSplitter.AddPaneB('<div class="p-3"><h5>Editor</h5>...</div>');
WebModule.Response := oSplitter.HTML; // two panes + gutter + scoped CSS + drag script
finally
oSplitter.Free;
end;
end;
PersistKey merita una menzione a parte: impostalo e la posizione della barra di trascinamento scelta da un visitatore verrà ricordata lato client e ripristinata alla visita successiva, così un layout di lavoro resta invariato tra una sessione e l'altra senza dover scrivere codice di gestione dello stato.
Provali
Tutti e quattro sono disponibili nell'attuale release di sgcWebSockets, con documentazione completa, riferimenti alle proprietà chiave ed esempi di codice Delphi/C++Builder/.NET sulle rispettive pagine: TreeGrid, PivotTable, ActivityFeed e Splitter. La matrice completa delle funzionalità elenca ora 83 componenti in nove famiglie — il prossimo articolo di questa serie tratta i nuovi grafici e i codici visivi.
Domande, feedback o serve aiuto con la migrazione? Contattaci — riceverai una risposta da chi ha scritto il codice.
