sgcHTML ist gerade um 25 Komponenten gewachsen, und dies ist der erste von fünf Beiträgen, die sie thematisch vorstellen. Wir beginnen mit vier Komponenten, die sich um strukturierte, sich ändernde Daten drehen: TreeGrid für hierarchische Zeilen, PivotTable für gruppierte Aggregate, ActivityFeed für einen Live-Strom von Ereignissen und Splitter für das größenveränderbare Zwei-Bereiche-Layout, das so oft all das oben Genannte aufnehmen muss. Jede der folgenden Komponenten ist eine reine Delphi-/C++Builder-/.NET-Klasse: Eigenschaften setzen, HTML aufrufen, Bootstrap-5-Markup zurückbekommen. Kein JavaScript-Framework, kein Build-Schritt.
TreeGrid — einklappbare hierarchische Tabellen
TsgcHTMLComponent_TreeGrid rendert eine Bootstrap-Tabelle, deren Zeilen sich einklappen und ausklappen lassen, aufgebaut aus einfachen Id-/ParentId-Knotenpaaren — derselben Form, die Sie bereits für Organigramme, Kategoriebäume oder verschachtelte Stücklisten-Daten verwenden. Deklarieren Sie eine TsgcHTMLTreeGridColumn pro sichtbarem Feld und fügen Sie dann Knoten hinzu; die Eltern-Kind-Beziehung zeichnet den Baum und die Umschaltpfeile für Sie.
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');
Wenn Ihre Daten bereits in einer selbstreferenzierenden Tabelle liegen, können Sie die manuellen Knotenaufrufe ganz überspringen und LoadFromDataSet einfach die Namen des Eltern- und des Kindfelds übergeben — die Methode durchläuft das Dataset und baut den gesamten Baum in einem Durchgang auf.
PivotTable — gruppieren, aggregieren, summieren
TsgcHTMLComponent_PivotTable nimmt flache Zeilen entgegen und erzeugt daraus eine gruppierte, aggregierte Tabelle mit Zeilensummen, Spaltensummen und einer Gesamtsumme, den Bericht, den Sie sonst von Hand in einer Tabellenkalkulation erstellen würden. Sie deklarieren RowFields und ColumnFields zum Gruppieren, ein oder mehrere Measures zum Aggregieren (Summe, Durchschnitt, Anzahl, Minimum, Maximum) sowie eine DataFields-Liste, die angibt, welche Quellspalten tatsächlich gelesen werden.
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);
Alles, was nach AddRow (oder LoadFromDataSet) kommt, wird für Sie berechnet: Die Komponente gruppiert die Rohzeilen, wendet die gewählte Aggregation pro Zelle an und fügt automatisch die Zeilen-, Spalten- und Gesamtsummenzeilen bzw. -spalten hinzu.
ActivityFeed — ein live aktualisierbarer Ereignisstrom
TsgcHTMLComponent_ActivityFeed rendert eine nach neuesten Einträgen sortierte Liste von "Wer hat was getan"-Einträgen mit relativen Zeitstempeln ("vor 3 Min.", clientseitig live gehalten), und ist von Anfang an dafür ausgelegt, per WebSockets aktualisiert zu werden, statt nur einmal gerendert zu werden. MaxItems hält sie als Ringpuffer — sobald das Limit erreicht ist, fällt der älteste Eintrag automatisch heraus.
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);
Diese letzte Zeile ist der interessante Teil: GetLastItemFragmentHTML rendert nur den neuesten Eintrag als Out-of-Band-htmx-Fragment, und BroadcastFragment sendet ihn an jeden offenen Browser über den sgcWebSockets-Server, den Sie ohnehin schon betreiben. Kein Polling, keine separate Echtzeit-Schicht, die verdrahtet werden müsste.
Splitter — die Zwei-Bereiche-Hülle, die alles zusammenhält
TsgcHTMLComponent_Splitter ist das Layout-Element: zwei größenveränderbare Bereiche mit einem ziehbaren Griff dazwischen, horizontal oder vertikal, mit Pixel-Mindestgrößen auf jeder Seite, damit keiner der Bereiche auf null gezogen werden kann. Es ist genau die Hülle, die ein TreeGrid-plus-Detailbereich- oder ein Dateiliste-plus-Editor-Bildschirm braucht.
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 verdient besondere Erwähnung: Setzen Sie diese Eigenschaft, und die Griffposition, die ein Besucher zieht, wird clientseitig gespeichert und bei seinem nächsten Besuch wiederhergestellt, sodass ein Arbeitsbereich-Layout über Sitzungen hinweg erhalten bleibt, ohne dass Sie irgendeinen Code zur Zustandsverwaltung schreiben müssen.
Jetzt ausprobieren
Alle vier sind Teil des aktuellen sgcWebSockets-Release, mit vollständiger Dokumentation, Referenzen der wichtigsten Eigenschaften und Delphi-/C++Builder-/.NET-Codebeispielen auf ihren eigenen Seiten: TreeGrid, PivotTable, ActivityFeed und Splitter. Die vollständige Feature-Matrix listet nun 83 Komponenten in neun Familien — der nächste Beitrag dieser Serie behandelt die neuen Diagramme und visuellen Codes.
Fragen, Feedback oder Hilfe bei der Migration? Nehmen Sie Kontakt auf — Sie erhalten eine Antwort von den Leuten, die den Code geschrieben haben.
