Angular Spreadsheet Component | ng-hub-ui-spreadsheet
Editable Angular spreadsheet with Excel's keyboard, rectangular selection, a clipboard that round-trips with Excel, frozen panes and merged cells. MIT, accessible, no paid tier.
API-Referenz
Hier ist der vollständige Vertrag für spreadsheet: alles, was du binden, abhören, projizieren und gestalten kannst, an einem Ort gesammelt. Verdrahte, was du brauchst, und gestalte, was du willst — es ist standalone und Signals-freundlich.
Inputs
Stelle spreadsheet mit 31 Inputs ein. Binde sie wie jeden Angular-@Input.
| Name | Typ | Standard | Beschreibung |
|---|---|---|---|
rowsErforderlich | readonly TRow[] | — | The rows, as you hold them. The sheet never writes to them. |
columnsErforderlich | readonly HubSpreadsheetColumn<TRow>[] | — | What each column shows and allows. |
rowKeyErforderlich | (row: TRow) => string | — | A stable name per row, so a save state follows its row across a reload. |
errors | Record<string, string> | {} | A validation message per cell, keyed like `states`. Owned by the host, because validation belongs to whoever knows what the data means. |
states | Record<string, HubSpreadsheetCellState> | {} | Save state per cell, keyed by the row key and the column alias joined by a tab. |
decimalMark | ',' | '.' | ',' | The character this reader types decimals with. Only consulted when a pasted value carried no raw number. |
emptyText | string | '' | What an empty sheet says. |
pageSize | number | 10 | How many rows Page Up and Page Down travel. |
readonly | boolean | false | Turns off every editor, whatever the individual cells say. |
frozenColumns | number | 0 | How many columns stay pinned to the leading edge, counted the way Excel freezes panes. |
frozenRows | number | 0 | How many rows stay pinned below the header. |
spans | readonly HubGridSpan[] | [] | Merged blocks, each named by its top-left corner and its extent. |
fillHandle | boolean | false | Whether the sheet offers the fill handle at the corner of the selection. |
canUndo | boolean | false | Whether undo is available. The sheet only asks; the owner keeps the history. |
canRedo | boolean | false | Whether redo is available. |
resizableColumns | boolean | false | Whether a reader may drag a column's trailing edge to widen or narrow it. |
reorderableColumns | boolean | false | Whether a reader may drag a header to put the column somewhere else. |
columnWidths | Record<string, number> | {} | The width of each column in pixels, keyed by alias. Two-way: the sheet writes it as the reader drags, and you may persist it. |
minColumnWidth | number | 48 | The narrowest a column may be dragged. |
virtual | boolean | false | Zeichnet nur die Zeilen, die das Sichtfenster abdeckt, und reserviert die Höhe der übrigen — für ein Blatt mit Tausenden. Standardmäßig aus, und es kostet etwas: die Zeilen außerhalb der Sicht stehen nicht im Dokument, also erreichen die Suche des Browsers und der Druck nur das Gezeichnete, und die Zeilen müssen gleich hoch sein. Das Einschalten gibt dem Blatt außerdem 24rem Höhe, denn frei wachsend ist es so hoch wie sein Inhalt und dann gibt es nichts zu virtualisieren. |
rowHeight | number | 0 | Wie hoch eine Zeile beim Virtualisieren ist, in Pixeln. Null misst die erste gezeichnete Zeile, was passt, solange die Zeilen gleich sind. Für ein im Verborgenen gebautes Blatt angeben: eine nie gesetzte Zeile misst null, und ein aus null errechnetes Fenster ist das ganze Blatt. |
formulas | boolean | false | Ob eine Zelle, deren Wert mit = beginnt, eine Formel ist. Aus, bis darum gebeten wird: eine Textspalte darf =1+1 durchaus als das enthalten, was jemand geschrieben hat. Eine Formel kann eine Spalte über ihren Alias benennen — =[units] * [price], =SUM([total:]) —, was nach dem Verschieben einer Spalte oder dem Umschreiben einer Überschrift dasselbe bedeutet, oder über eine Koordinate, wie ein Tabellenprogramm sie schreibt — =ROUND(SUM(E1:E4), 2) —, die sagt, wo eine Spalte war, nicht welche sie ist; rewriteRowFormulas() zieht diese mit dem Blatt mit, und ein Verweis auf etwas Gelöschtes wird #REF! statt still die Nachbarspalte zu lesen. Der Doppelpunkt liest die ganze Spalte und lässt die fragende Zelle aus; jeder andere Kreis erscheint als #CYCLE!, statt verfolgt zu werden. |
mergeable | boolean | false | Ob die Leserin Zellen verbinden und wieder trennen darf, angeboten im Kontextmenü — dafür braucht es auch contextMenu. Aus, bis darum gebeten wird, wie jede Strukturänderung. |
editOn | 'click' | 'double-click' | 'double-click' | Was den Editor mit dem Zeiger öffnet. Ein Doppelklick als Vorgabe, die Tabellenkalkulations-Konvention: ein Klick wählt die Zelle und lässt ihren Wert in Ruhe, das erste getippte Zeichen öffnet den Editor und landet darin, und eine Zelle mit Liste oder Datum zeigt eine Marke, die sie mit einem Klick öffnet. Mit click öffnet schon der erste, für ein Blatt, in das geschrieben wird. So oder so öffnet der Editor beim Loslassen und nur, wenn der Zeiger nicht gewandert ist, sodass das Aufziehen eines Bereichs weiterhin funktioniert. |
contextMenu | boolean | false | Whether a right-click offers the menu of structural changes. Each entry still has to pass the permissions, and the menu is never opened empty. |
columns[].kind | 'text' | 'number' | 'currency' | 'date' | 'boolean' | 'select' | 'text' | How the column shows its values and reads back what is typed. A select column opens a list and refuses anything not on it; a boolean draws a checkbox that turns over in one click; a date opens a date field. |
columns[].formula | string | — | Eine Formel, aus der jede Zelle dieser Spalte entsteht — im Code deklariert statt in den Daten gehalten: eine Zeilensumme, eine Steuer, ein laufender Saldo. Das Gegenteil einer Formel, die eine Leserin tippt und die ein Wert der Zeile ist. Zellen einer solchen Spalte lassen sich gar nicht bearbeiten: eine zu ersetzen verlöre die Formel für genau diese Zeile, und das merkt niemand, bis die Summen nicht mehr stimmen. Braucht formulas am Blatt. |
columns[].placeholder | string | — | A hint shown in an empty cell of this column while the pointer rests on it. Never in place of a value: a column of hints always on screen reads as a column of content. |
columns[].options | readonly HubSpreadsheetOption[] | — | The answers a select column offers. A cell shows the matching label rather than the stored value, and falls back to the raw value when nothing matches, so a row holding a retired option stays readable. |
structure | HubSpreadsheetStructureOptions<TRow> | {} | Which structural changes the reader may ask for. Everything is refused until allowed. |
retiredColumnKeys | readonly string[] | [] | Aliases of columns that were deleted, so a new one never inherits a dead identity. |
Outputs
Reagiere darauf, was spreadsheet tut — 12 Events, an die du deine Logik anknüpfen kannst.
| Name | Typ | Beschreibung |
|---|---|---|
commit | HubSpreadsheetCommit<TRow> | A cell took a new value, and it differs from the one it held. |
pasted | HubSpreadsheetPaste<TRow> | A block was pasted, with counts of what fell outside the sheet and what was not a number. |
cleared | HubSpreadsheetCellRef<TRow>[] | Delete was pressed over a selection; carries the editable cells it covered. |
selectionChange | HubGridRange | null | The selected rectangle changed. |
filled | HubSpreadsheetPaste<TRow> | A drag of the fill handle finished, reported as one event for the whole gesture. |
undoRequested | void | The reader asked to undo. |
redoRequested | void | The reader asked to redo. |
columnMoved | { from: number; to: number; key: string; keys: string[] } | A column was dropped somewhere else. Reported, not applied: the payload carries the aliases already reordered. |
mergeRequested | HubSpreadsheetMergeRequest | Es wurde um das Verbinden einer Auswahl gebeten. Die Nutzlast trägt den neuen Block und die Anker der Blöcke, die er schluckt: wer nur anhängt, hat zwei Blöcke auf derselben Zelle. applySpanMerge() erledigt es in einer Zeile. |
unmergeRequested | readonly HubGridCoords[] | Es wurde darum gebeten, die von der Auswahl berührten Blöcke zu trennen, benannt über ihre Anker. applySpanUnmerge() wendet es an. |
insertRequested | HubSpreadsheetInsertRequest | The reader asked to add rows or columns. |
deleteRequested | HubSpreadsheetDeleteRequest | The reader asked to remove rows or columns, naming the aliases at stake. |
Methoden
Steuere spreadsheet aus dem Code heraus — 17 Methoden auf der programmatischen Schnittstelle.
| Name | Signatur | Rückgabe | Beschreibung |
|---|---|---|---|
sheetToXlsx | sheetToXlsx<TRow>(rows, columns, options?: HubXlsxOptions): Uint8Array | Uint8Array | Das Blatt als echtes .xlsx, als Bytes. Zahlen bleiben Zahlen, und eine Datumsspalte wird ein Datum — was ein CSV nicht tragen kann. Geschrieben wird, was die Zelle zeigt: eine Listenspalte exportiert ihr Label, eine Formel ihr Ergebnis, sofern nicht values: 'stored' das Gespeicherte verlangt. |
downloadXlsx | downloadXlsx(filename: string, bytes: Uint8Array): void | — | Gibt der Leserin diese Arbeitsmappe zum Speichern und hängt .xlsx an, wenn es fehlt. Hier, weil jeder Host die gleichen sechs Zeilen schreibt und die Object-URL nicht freigibt. |
xlsxToRecords | xlsxToRecords<TRow>(bytes, columns, options?: HubXlsxReadOptions): Promise<Array<Record<string, string>>> | Promise<Array<Record<string, string>>> | Liest das erste Blatt einer Arbeitsmappe in Zeilen, die nach Spaltenalias benannt sind, und macht die zwei Dinge rückgängig, die ein CSV nie tut: gemeinsame Zeichenketten und Datumsangaben als Tageszahl. Asynchron, weil eine echte Arbeitsmappe komprimiert ist und der Dekompressor der der Plattform ist. |
sheetToCsv | sheetToCsv<TRow>(rows, columns, options?: HubCsvOptions): string | string | Das Blatt als CSV. Ohne angegebenes Trennzeichen kommt es vom Dezimalzeichen — ein Komma kann nicht trennen, was es auch gliedert — also bekommt eine Datei, die dort gelesen wird, wo man 1.234,56 schreibt, Semikolons. |
downloadText | downloadText(filename: string, text: string, type?: string): void | — | Gibt der Leserin eine Textdatei zum Speichern, mit der Byte-Order-Mark, die ein Tabellenprogramm dazu bringt, sie als UTF-8 statt als Zeichensalat zu öffnen. |
csvToRecords | csvToRecords<TRow>(text, columns, options?: HubCsvOptions): Array<Record<string, string>> | Array<Record<string, string>> | Liest ein CSV in Zeilen nach Spaltenalias und schnuppert das Trennzeichen in der ersten Zeile, statt es anzunehmen. Überschriften werden zuerst über die Überschrift, dann über den Alias zugeordnet, ohne Rücksicht auf Groß- und Kleinschreibung oder Leerraum. |
sheetValues | sheetValues<TRow>(rows, columns, options?: HubSheetValuesOptions): HubSpreadsheetValue[][] | HubSpreadsheetValue[][] | Was jede Zelle enthält, mit ausgerechneten Formeln und einer gescheiterten so geschrieben, wie ein Tabellenprogramm sie schreibt. Genau das lesen beide Exporte — offengelegt für einen Host, der seine eigene Datei schreibt. |
evaluateSheet | evaluateSheet<TRow>(input: HubFormulaSheetInput<TRow>): HubFormulaSheet | HubFormulaSheet | Führt jede Formel eines Blattes aus, in der Reihenfolge, die die Abhängigkeiten verlangen, bei Bedarf berechnet und behalten. Ein Kreis wird erkannt, sobald er sich schließt, statt verfolgt zu werden. Offengelegt für einen Host, der die Ergebnisse ohne Darstellung braucht. |
rewriteRowFormulas | rewriteRowFormulas<TRow extends object>(rows, change: HubFormulaShapeChange): TRow[] | TRow[] | Verschiebt jede Koordinate in den Zeilen, wenn das Blatt seine Form ändert: eine verschobene Spalte, eingefügte oder gelöschte Zeilen und Spalten. Ein Verweis auf Gelöschtes wird #REF! statt still die Nachbarspalte zu lesen. Der Host ruft es auf, denn die Zeilen sind seine. |
mergeRequestFor | mergeRequestFor(spans, range): HubSpreadsheetMergeRequest | null | HubSpreadsheetMergeRequest | null | Der Block, zu dem eine Auswahl würde, und die Anker, die er verschluckt; null bei einer einzelnen Zelle. Die verschluckten Anker sind der Teil, den ein Host allein falsch macht: angehängt, ohne sie zu entfernen, beanspruchen zwei Blöcke dieselbe Zelle. |
applySpanMerge | applySpanMerge(spans, request: HubSpreadsheetMergeRequest): HubGridSpan[] | HubGridSpan[] | Die Liste der Blöcke nach dieser Verbindung, ohne die verschluckten. |
applySpanUnmerge | applySpanUnmerge(spans, anchors: readonly HubGridCoords[]): HubGridSpan[] | HubGridSpan[] | Die Liste der Blöcke, nachdem diese aufgelöst sind. |
spreadsheetRecords | spreadsheetRecords<TRow>(rows, columns): HubSpreadsheetRecord[] | HubSpreadsheetRecord[] | Jede Zeile als einfache Daten nach Spaltenalias, damit man mit { price: 12, units: 3 } arbeitet statt Koordinaten über die Position auf eigene Felder abzubilden. |
parseForColumn | parseForColumn<TRow>(text, column, decimalMark: ',' | '.'): HubSpreadsheetValue | undefined | HubSpreadsheetValue | undefined | Was eine getippte oder eingefügte Zeichenkette in einer Spalte wert ist: der Wert, null für eine leere Zelle und undefined für etwas, das die Spalte nicht halten kann — womit der Aufrufer ablehnen kann, statt Unsinn zu speichern. |
nextColumnKey | nextColumnKey(taken: Iterable<string>, prefix?: string): string | string | Ein Alias, der noch nie zu einer anderen Spalte gehört hat: eine neue Spalte verwendet also keinen stillgelegten Namen wieder, und kein gespeicherter Verweis kann auf das Falsche zeigen. |
danglingColumnKeys | danglingColumnKeys<TRow>(referenced: Iterable<string>, columns): string[] | string[] | Verweise, die auf keine Spalte mehr zeigen — also das, was eine Löschung ins Leere zeigen ließe. |
provideHubSpreadsheetControls | provideHubSpreadsheetControls(adapter: HubSpreadsheetControlsAdapter): EnvironmentProviders | EnvironmentProviders | Registriert einen Control-Adapter; jede Listenspalte öffnet dann mit ihm — suchbar, schon offen, mit dem Zeichen, das die Zelle geöffnet hat, in ihrem Suchfeld. Ohne Adapter zeichnet das Blatt seine native Liste, was es allein installierbar hält. |
Templates
Mach es zu deinem — 2 Template-Slots lassen dich eigenes Markup projizieren.
| Name | Beschreibung | Beispiel |
|---|---|---|
hubSpreadsheetEditor | Ersetzt den Editor einer Spalte, benannt über ihren Alias. Das Template bekommt den Wert, das Paar commit und cancel sowie `seed` — das Zeichen, mit dem die Zelle durch Tippen geöffnet wurde und das in das Suchfeld eines Pickers gehört. Es bleibt offen, bis es eines von beiden aufruft. Das Blatt hängt von nichts ab, was darin steht. | <ng-template hubSpreadsheetEditor="assignee" let-value let-seed="seed" let-commit="commit">
<hub-select [items]="people" autoOpen [initialSearchTerm]="seed ?? null" [ngModel]="value" (ngModelChange)="commit($event)" />
</ng-template> |
hubSpreadsheetCell | Zeichnen Sie die Zellen einer Spalte selbst, solange sie nicht bearbeitet werden: ein Abzeichen für einen Zustand, ein Avatar neben einem Namen, eine Reihe von Aktionen. Das Gegenstück zu hubSpreadsheetEditor, das die andere Hälfte des Zellenlebens abdeckt. Die Zwischenablage trägt weiterhin den Wert und nicht die Zeichnung, und die Zelle bleibt das, worauf die Tastatur steht. Über hubSpreadsheetCellRows die Zeilen übergeben, und der Compiler weiß, was `row` ist. | <ng-template hubSpreadsheetCell="state" let-value>
<hub-badge variant="soft" [color]="colourOf(value)">{{ labelOf(value) }}</hub-badge>
</ng-template> |