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

Вот полный контракт spreadsheet: всё, что можно привязать, прослушать, спроецировать и тематизировать, собрано в одном месте. Подключите то, что нужно, и оформите так, как хотите — это standalone и дружит с сигналами.

Входные свойства

Настройте spreadsheet с помощью 31 входных свойств. Привязывайте их, как любой @Input в Angular.

ИмяТипПо умолчаниюОписание
rowsОбязательноreadonly TRow[]—The rows, as you hold them. The sheet never writes to them.
columnsОбязательноreadonly HubSpreadsheetColumn<TRow>[]—What each column shows and allows.
rowKeyОбязательно(row: TRow) => string—A stable name per row, so a save state follows its row across a reload.
errorsRecord<string, string>{}A validation message per cell, keyed like `states`. Owned by the host, because validation belongs to whoever knows what the data means.
statesRecord<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.
emptyTextstring''What an empty sheet says.
pageSizenumber10How many rows Page Up and Page Down travel.
readonlybooleanfalseTurns off every editor, whatever the individual cells say.
frozenColumnsnumber0How many columns stay pinned to the leading edge, counted the way Excel freezes panes.
frozenRowsnumber0How many rows stay pinned below the header.
spansreadonly HubGridSpan[][]Merged blocks, each named by its top-left corner and its extent.
fillHandlebooleanfalseWhether the sheet offers the fill handle at the corner of the selection.
canUndobooleanfalseWhether undo is available. The sheet only asks; the owner keeps the history.
canRedobooleanfalseWhether redo is available.
resizableColumnsbooleanfalseWhether a reader may drag a column's trailing edge to widen or narrow it.
reorderableColumnsbooleanfalseWhether a reader may drag a header to put the column somewhere else.
columnWidthsRecord<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.
minColumnWidthnumber48The narrowest a column may be dragged.
virtualbooleanfalseРисует только строки, попадающие в окно просмотра, и резервирует высоту остальных — для листа в тысячи строк. По умолчанию выключено, и за это есть плата: строк вне видимости нет в документе, поэтому поиск браузера и печать достают лишь нарисованное, а строки должны быть одной высоты. Включение также задаёт листу высоту 24rem: растущий свободно, он равен своему содержимому, и виртуализировать нечего.
rowHeightnumber0Высота строки при виртуализации, в пикселях. Ноль измеряет первую нарисованную строку — это верно, пока строки одинаковы. Укажите её для листа, собираемого скрытым: ни разу не размеченная строка измеряется нулём, а окно, посчитанное от нуля, — это весь лист.
formulasbooleanfalseСчитается ли формулой ячейка, значение которой начинается с =. Выключено, пока не попросят: текстовый столбец вполне может хранить =1+1 как то, что кто-то написал. Формула может обратиться к столбцу по алиасу — =[units] * [price], =SUM([total:]) — и после переноса столбца или переписанного заголовка она значит то же самое, либо по координате, как пишет табличный редактор — =ROUND(SUM(E1:E4), 2) —, а координата говорит, где столбец был, а не какой он; rewriteRowFormulas() переносит такие вместе с листом, а ссылка на удалённое становится #REF!, а не читает втихую соседний. Двоеточие читает весь столбец, исключая спрашивающую ячейку; любой другой круг показывается как #CYCLE!, а не преследуется.
mergeablebooleanfalseМожет ли читатель объединять ячейки и разделять их — предлагается в контекстном меню, так что нужен и contextMenu. Выключено, пока не попросят, как любое изменение структуры.
editOn'click' | 'double-click''double-click'Что открывает редактор указателем. По умолчанию двойной щелчок — соглашение таблиц: щелчок выбирает ячейку и оставляет её значение в покое, первый набранный символ открывает редактор и попадает в него, а ячейка со списком или датой показывает значок, открывающий её одним щелчком. Значение click открывает с первого щелчка — для листа, который существует, чтобы в нём печатали. В обоих случаях редактор открывается при отпускании и только если указатель не двигался, поэтому выделение диапазона протягиванием работает по-прежнему.
contextMenubooleanfalseWhether 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[].formulastring—Формула, из которой выводится каждая ячейка этого столбца, объявленная в коде, а не хранимая в данных: итог строки, налог, текущий остаток. Противоположность формуле, которую набирает читатель, — та является значением строки и меняется как любое другое. Ячейки такого столбца вообще нельзя редактировать: замена одной потеряла бы формулу лишь в этой строке, и этого никто не заметит, пока итоги не перестанут сходиться. Требует formulas на листе.
columns[].placeholderstring—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[].optionsreadonly 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.
structureHubSpreadsheetStructureOptions<TRow>{}Which structural changes the reader may ask for. Everything is refused until allowed.
retiredColumnKeysreadonly string[][]Aliases of columns that were deleted, so a new one never inherits a dead identity.

События

Реагируйте на то, что делает spreadsheet, — 12 событий, к которым можно подключить вашу логику.

ИмяТипОписание
commitHubSpreadsheetCommit<TRow>A cell took a new value, and it differs from the one it held.
pastedHubSpreadsheetPaste<TRow>A block was pasted, with counts of what fell outside the sheet and what was not a number.
clearedHubSpreadsheetCellRef<TRow>[]Delete was pressed over a selection; carries the editable cells it covered.
selectionChangeHubGridRange | nullThe selected rectangle changed.
filledHubSpreadsheetPaste<TRow>A drag of the fill handle finished, reported as one event for the whole gesture.
undoRequestedvoidThe reader asked to undo.
redoRequestedvoidThe 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.
mergeRequestedHubSpreadsheetMergeRequestЧитатель попросил объединить выделение. В данных — создаваемый блок и якоря тех, что он поглощает: если просто добавить новый, два блока будут претендовать на одну ячейку. applySpanMerge() делает это одной строкой.
unmergeRequestedreadonly HubGridCoords[]Читатель попросил разделить блоки, которых касается выделение, названные по их якорям. Применяется через applySpanUnmerge().
insertRequestedHubSpreadsheetInsertRequestThe reader asked to add rows or columns.
deleteRequestedHubSpreadsheetDeleteRequestThe reader asked to remove rows or columns, naming the aliases at stake.

Методы

Управляйте spreadsheet из кода — 17 методов его программного интерфейса.

ИмяСигнатураВозвращаетОписание
sheetToXlsxsheetToXlsx<TRow>(rows, columns, options?: HubXlsxOptions): Uint8ArrayUint8ArrayЛист как настоящий .xlsx, в байтах. Число остаётся числом, а столбец дат становится датой — того CSV не умеет. Записывается то, что показывает ячейка: столбец-список отдаёт свою подпись, формула — свой результат, если только values: 'stored' не потребует хранимого.
downloadXlsxdownloadXlsx(filename: string, bytes: Uint8Array): void—Отдаёт читателю эту книгу на сохранение, добавляя .xlsx к имени, если его нет. Здесь потому, что каждый хост пишет одни и те же шесть строк и забывает освободить object URL.
xlsxToRecordsxlsxToRecords<TRow>(bytes, columns, options?: HubXlsxReadOptions): Promise<Array<Record<string, string>>>Promise<Array<Record<string, string>>>Читает первый лист книги в строки с ключами по алиасам столбцов, отменяя две вещи, которых в CSV не бывает: общие строки и даты, хранимые как число дней. Асинхронная, потому что настоящая книга сжата, а распаковщик берётся у платформы.
sheetToCsvsheetToCsv<TRow>(rows, columns, options?: HubCsvOptions): stringstringЛист как CSV. Если разделитель не задан, он выводится из десятичного знака — запятая не может разделять то, что она же и разделяет внутри числа, — поэтому файл для тех, кто пишет 1.234,56, получает точку с запятой.
downloadTextdownloadText(filename: string, text: string, type?: string): void—Отдаёт читателю текстовый файл на сохранение, с меткой порядка байтов, из-за которой табличный редактор откроет его как UTF-8, а не как кракозябры.
csvToRecordscsvToRecords<TRow>(text, columns, options?: HubCsvOptions): Array<Record<string, string>>Array<Record<string, string>>Читает CSV в строки с ключами по алиасам столбцов, определяя разделитель по первой строке, а не предполагая его. Заголовки сопоставляются сперва по заголовку, затем по алиасу, без учёта регистра и пробелов по краям.
sheetValuessheetValues<TRow>(rows, columns, options?: HubSheetValuesOptions): HubSpreadsheetValue[][]HubSpreadsheetValue[][]Что содержит каждая ячейка — с посчитанными формулами, а неудавшаяся записана так, как её пишет табличный редактор. Именно это читают оба экспорта; открыто для хоста, который пишет собственный файл.
evaluateSheetevaluateSheet<TRow>(input: HubFormulaSheetInput<TRow>): HubFormulaSheetHubFormulaSheetВыполняет все формулы листа в том порядке, какой требуют зависимости, считая по необходимости и запоминая. Круг замечается в момент замыкания, а не преследуется. Открыто для хоста, которому нужны ответы без отрисовки.
rewriteRowFormulasrewriteRowFormulas<TRow extends object>(rows, change: HubFormulaShapeChange): TRow[]TRow[]Переносит все координаты в строках, когда лист меняет форму: столбец переехал, строки или столбцы вставлены или удалены. Ссылка на удалённое становится #REF!, а не читает втихую соседний столбец. Вызывает её хост, потому что строки его.
mergeRequestFormergeRequestFor(spans, range): HubSpreadsheetMergeRequest | nullHubSpreadsheetMergeRequest | nullБлок, которым стало бы выделение, и якоря, которые он поглощает; null для одной ячейки. Поглощённые якоря — как раз то, что хост делает неверно сам: добавив блок и не убрав их, получаешь два блока на одну ячейку.
applySpanMergeapplySpanMerge(spans, request: HubSpreadsheetMergeRequest): HubGridSpan[]HubGridSpan[]Список блоков после этого объединения, без поглощённых.
applySpanUnmergeapplySpanUnmerge(spans, anchors: readonly HubGridCoords[]): HubGridSpan[]HubGridSpan[]Список блоков после того, как эти разъединены.
spreadsheetRecordsspreadsheetRecords<TRow>(rows, columns): HubSpreadsheetRecord[]HubSpreadsheetRecord[]Каждая строка как простые данные с ключами по алиасам столбцов, чтобы работать с { price: 12, units: 3 }, а не сопоставлять координаты со своими полями по позиции.
parseForColumnparseForColumn<TRow>(text, column, decimalMark: ',' | '.'): HubSpreadsheetValue | undefinedHubSpreadsheetValue | undefinedЧего стоит в столбце набранная или вставленная строка: значение, null для пустой ячейки и undefined для того, чего столбец держать не может, — чтобы вызывающий мог отказать, а не сохранить мусор.
nextColumnKeynextColumnKey(taken: Iterable<string>, prefix?: string): stringstringАлиас, который никогда не принадлежал другому столбцу: новый столбец не переиспользует отставное имя, и ни одна сохранённая ссылка не сможет указать не туда.
danglingColumnKeysdanglingColumnKeys<TRow>(referenced: Iterable<string>, columns): string[]string[]Ссылки, которые больше не ведут ни к одному столбцу, — то есть то, что удаление оставило бы висеть в пустоте.
provideHubSpreadsheetControlsprovideHubSpreadsheetControls(adapter: HubSpreadsheetControlsAdapter): EnvironmentProvidersEnvironmentProvidersРегистрирует адаптер контролов, и тогда каждый столбец-список открывается через него: с поиском, уже раскрытым, и с символом, открывшим ячейку, в поле поиска. Без адаптера лист рисует свой родной список — именно это и позволяет ставить библиотеку саму по себе.

Шаблоны

Сделайте его своим — 2 слотов шаблона позволяют проецировать собственную разметку.

ИмяОписаниеПример
hubSpreadsheetEditorЗаменяет редактор одного столбца, названного по его алиасу. Шаблон получает значение, пару commit и cancel и `seed` — символ, которым читатель открыл ячейку, начав печатать; ему место в поле поиска выбиралки. Шаблон остаётся открытым, пока не вызовет одно из двух. Лист не зависит от того, что вы в него положите.<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Рисуйте ячейки столбца сами, пока их не редактируют: значок для состояния, аватар рядом с именем, ряд действий. Пара к hubSpreadsheetEditor, который закрывает другую половину жизни ячейки. В буфер обмена по-прежнему уходит значение, а не рисунок, и клавиатура по-прежнему стоит на ячейке. Передайте строки через hubSpreadsheetCellRows — и компилятор будет знать, что такое `row`.<ng-template hubSpreadsheetCell="state" let-value> <hub-badge variant="soft" [color]="colourOf(value)">{{ labelOf(value) }}</hub-badge> </ng-template>