Readonly tableAdds new rows to the end of the table body.
Only works with an AreaModelObjectArray body – logs a console warning otherwise. Does not repaint and resets filtering and sorting of the body: afterwards call externalFilterChanged (if a filter function is set) and repaintHard (the row count changed).
The type of elements in the rows array
An array of data objects to append to the table's existing rows
This method appends the provided array of data objects to the end of the existing rows in the table body. It only works with tables that use AreaModelObjectArray as their body model.
tableApi.addRows([{ id: 100_001, customer: "New GmbH" }]);
tableApi.repaintHard();
tableApi.ensureRowIsVisible(tableApi.getDisplayedRowCount() - 1);
Inserts new rows at a specific position in the table body.
Only works with an AreaModelObjectArray body – logs a console warning otherwise. Does not repaint and resets filtering and sorting of the body: afterwards call externalFilterChanged (if a filter function is set) and repaintHard (the row count changed).
The type of elements in the rows array
An array of data objects to insert into the table
The index position where the new rows should be inserted
This method inserts the provided array of data objects at the specified index position in the table body. Existing rows at or after the specified index will be shifted down. It only works with tables that use AreaModelObjectArray as their body model.
Speaks a message to screen reader users through the live region of the table (role="status",
polite – it does not interrupt). The table itself announces sorting, filter results, copying and
"select all"; use this for your own events, e.g. after loading or saving data.
plain text (not HTML)
fetch("/api/orders").then(r => r.json()).then(orders => {
tableApi.setRows(orders);
tableApi.announce(`${orders.length} orders loaded`);
});
1.1.91
Writes values into body cells like the cell editor does – the one way the table changes cells (editor, fill
handle and batch commit use it too): cells that are not editable are skipped (editable, calculated columns
without valueSetter, group and pivot rows), values run through editInputPipe and ColumnDefIf.validate
(invalid ones are not written), totals and calculated columns follow, and one onModelChanged reports all
cells with old and new values. In a batch edit the values are marked as pending instead.
Paste from the clipboard and undo/redo can be built on it: a CellChangeIf has the old and the new value.
display row index, column (index or property) and value per cell
Optional source: CellChangeSource = "api"reported in GeModelChangeEvent.source
Optional opts: { undoable: record the change for undo (TableOptionsIf.undo) – default:
UndoOptionsIf.recordApiChanges for "api", recorded for the other sources (since 1.1.99)
Optional undoable?: booleanthe written (batch edit: newly pending) cells
tableApi.applyCellChanges([
{ rowIndex: 0, column: "price", value: 9.9 },
{ rowIndex: 1, column: "price", value: 12 },
]);
// undo the last change set (from onModelChanged)
const undo = (changes: CellChangeIf[]) => tableApi.applyCellChanges(
changes.map(c => ({ rowIndex: c.rowIndex, column: c.property, value: c.oldValue })));
1.1.96
Applies a state from getState (or one written by hand) and repaints. Only the parts contained in the
state are applied (optionally only the given ones); the others stay as they are – pass a part empty to reset it,
e.g. sort: [] (clears the sort icons; the rows keep their current order). Unknown columns and rows are ignored. The change is stored (TableOptionsIf.autoRestoreOptions)
and reported (EventListenerIf.onStateChanged).
the state
Optional parts: TableStatePart[]only these parts, default: all parts of the state
// two saved views of the same table
const views: Record<string, TableStateIf> = {};
views["compact"] = tableApi.getState(["columns"]);
tableApi.applyState(views["compact"]);
tableApi.applyState({ sort: [{ property: "total", sortState: "desc" }] });
1.1.92
Records all changes fn makes (several applyCellChanges calls, a fill and a paste …) as one undo step.
Without the option fn just runs.
the label of the step ("Undo Import")
the changes
what fn returns
tableApi.batchUndoable("Import", () => {
tableApi.applyCellChanges(firstChanges, "api", { undoable: true });
tableApi.applyCellChanges(secondChanges, "api", { undoable: true });
});
1.1.99
True if there is an undone change to redo.
true if redo would redo something
redoButton.disabled = !tableApi.canRedo();
1.1.99
True if there is a change to undo – for buttons (or listen to onUndoStackChanged).
true if undo would undo something
undoButton.disabled = !tableApi.canUndo();
1.1.99
Discards the pending values of a batch edit (the cells show the data again) and ends a batch edit started with startBatchEdit.
discardButton.onclick = () => tableApi.cancelBatchEdit();
1.1.96
Clears body cells like Delete in Excel-style editing: editable cells get their empty value
(ColumnDefIf.emptyValue, default ""), one onModelChanged with the source "clear" (batch edit:
marked). Works without TableOptionsIf.editing too.
Optional ranges: CellRange[]body ranges; default: the selected cells, or the focused cell
the number of cleared cells, -1 if there were too many (EditingOptionsIf.maxClearCells)
tableApi.clearCells([new CellRange(0, 2, 9, 2)]); // rows 0–9 of column 2
1.1.99
Clears the current selection in the table.
This method removes any selected cells, rows, or columns in the table. It provides a convenient way to programmatically clear all selections in the table without having to access the underlying selection model directly.
When called, this method delegates to the table scope's clearSelection method with a parameter value of true, which ensures the table is automatically repainted after the selection is cleared.
// Clear all selections in the table (e.g. from a "reset" button)
tableApi.clearSelection();
Writes the pending values of a batch edit into the rows (through setValue, like the editor) and ends a batch edit
started with startBatchEdit: one onModelChanged (source "commit") with all cells, then
onBatchEditChanged with the type "commit". While a value is invalid (ColumnDefIf.validate) nothing is
written and the result is false (unless BatchEditOptionsIf.blockCommitOnInvalid is false).
true if written
if (!tableApi.commitBatchEdit()) {
alert("Please fix the marked cells first.");
}
1.1.96
Copies the selected cells (the last selected range, everything after Ctrl/⌘+A, otherwise the focused cell) with
the asynchronous clipboard API – for an own button or menu. Tab-separated text with Excel's quoting plus an HTML
table (ClipboardOptionsIf.html); pasting it into this table pastes the values. The browser may ask for
permission; Ctrl/⌘+C needs none (TableOptionsIf.clipboard). Works without the option (defaults).
resolves when the clipboard was written
copyButton.addEventListener("click", () => tableApi.copySelection());
1.1.99
Copies the selected data from the table to the clipboard.
This method leverages the copyService to extract and copy the currently selected data from the table. It works with the current selection state and focus position to determine what content should be copied.
The copied data is formatted as tab-separated text that can be pasted into spreadsheet applications like Excel or text editors while maintaining the tabular structure. Screen readers hear "12 cells copied" (AriaTextsIf.copied).
A promise that resolves with the copied data as a string. The string contains the tab-separated representation of the selected table data.
// Copy the currently selected data to clipboard
tableApi.copyToClipboard().then(content => {
console.log('Copied to clipboard:', content);
});
// Using with a button click handler
function onCopyButtonClick() {
if (tableApi) {
tableApi.copyToClipboard()
.then(content => {
console.log('Successfully copied data to clipboard');
// Optionally do something with the copied content
})
.catch(error => {
console.error('Failed to copy to clipboard:', error);
});
}
}
Like copySelection, and the cells are marked (dashed frame): the next paste of this content moves them –
the cut cells are emptied in the same change (source: "cut"). Escape or another change removes the mark.
resolves when the clipboard was written
cutButton.addEventListener("click", () => tableApi.cutSelection());
1.1.99
Deletes columns of a spreadsheet (at = letter index, A = 0) and adjusts the cell formulas. One undo step.
the first data column
Optional count: number = 1columns
true if deleted (false for other models)
tableApi.deleteColumns(2); // column C
1.1.99
Deletes rows of the sheet (at = row number − 1) and adjusts the cell formulas (a deleted reference becomes
#REF!). One undo step.
the first physical row
Optional count: number = 1rows
true if deleted
tableApi.deleteRows(2); // row 3
1.1.99
Releases the table: removes all DOM listeners (incl. the window resize listener), stops the resize observer, calls the cleanup functions of all cell renderers and clears the host element. The framework wrappers call it automatically when they are unmounted or receive a new table model.
// Plain TS: replace the table with a new model
tableApi.destroy();
const scope = new TableScope(hostDiv, newModel, new SimpleDomService(), new TableOptions(), new EventAdapter());
scope.firstInit();
Downloads the current table data as an Excel file.
This method extracts all visible data from the table (including header, body, and footer areas), converts it into a matrix format, and triggers a download of an Excel file containing this data.
The method works by:
The name of the Excel file to be downloaded (default: 'table.xlsx')
Optional metadata for the Excel file to specify the author (default: '')
resolves when the (compressed) file has been handed to the browser
// Download table data with default filename
tableApi.downloadExcel();
// Download table data with custom filename and author
tableApi.downloadExcel('sales-report.xlsx', 'John Doe');
// Download table data with custom filename only
tableApi.downloadExcel('quarterly-data.xlsx');
// cell formulas as Excel formulas (since 1.1.99, tableOptions.cellFormulas)
tableApi.downloadExcel('budget.xlsx', '', { cellFormulas: true });
Ensures that a column is fully visible by scrolling horizontally if necessary – the counterpart of ensureRowIsVisible. A column left of the viewport becomes the first visible column, a column right of it the last one. The vertical position is kept. The arrow keys use it for the focused cell.
Fixed columns (fixedLeftColumnCount / fixedRightColumnCount) are always visible, hidden columns
(setColumnVisible) can't be scrolled to: both return false.
The column index (model index over west + center + east)
true if the table was scrolled, false if the column was already visible
// show the column of a search hit (rowIndex/columnIndex from your own search)
const rowIndex = 1200, columnIndex = 14;
tableApi.ensureColumnIsVisible(columnIndex);
tableApi.ensureRowIsVisible(rowIndex);
scrollToIndex - scrolls a column and row to the top left
Ensures that a specific row is visible in the viewport by scrolling if necessary. This method checks if the target row is within the currently visible range and adjusts the scroll position if it's not visible.
The method performs the following:
The index of the row to make visible
Returns true if scrolling was needed and performed, false if the row was already visible
// Ensure row 5 is visible in the viewport
tableApi.ensureRowIsVisible(5);
Implicitly may throw if rowIndex is not a number or if required properties are undefined
Calculates a formula once against the sheet (not stored), e.g. for a preview: "=SUM(D2:D4)".
undefined without cell formulas.
the formula (the = is optional)
the result (a FormulaError for an error)
tableApi.evaluateFormula("=SUM(D2:D4)"); // 3895
1.1.99
Expands the detail rows of all shown master rows. The contents are created only when they scroll into view, so this is cheap also for many rows. No event per row.
the number of rows that were expanded
document.querySelector("#expand-all")!.addEventListener("click", () => tableApi.expandAllDetails());
1.1.97
Notifies the table that an external filter has been changed and needs to be applied.
This method is used when an external filter condition (defined by tableOptions.externalFilterFunction)
has changed and the table needs to refresh its display to reflect the new filtering criteria.
When called, this method:
// Define an external filter function in your table options
const tableOptions = {
externalFilterFunction: (row: MyRowType) => {
// Return true to include the row, false to filter it out
return row.status === 'active';
}
};
// Initialize your table with these options
const tableApi = new TableApi(tableScope);
// Later, when filter criteria change (e.g., in a search input handler)
function onFilterTextChanged() {
// Update the filter function if needed
tableOptions.externalFilterFunction = (row: MyRowType) => {
return row.name.includes(searchText);
};
// Apply the updated filter
tableApi.externalFilterChanged();
}
TableScope.externalFilterChanged
Fills cells next to a range – what dragging the fill handle does (TableOptionsIf.fillHandle is not
needed for this call): continues series (1, 2 → 3, 4; dates; Item 1 → Item 2) or copies, writes with
applyCellChanges (one onModelChanged, source "fill") and selects source and filled cells.
the source range (body display indices)
"down", "up", "right" or "left"
rows or columns to fill (clamped to the table)
Optional mode: FillMode = "auto""auto", "alternate" (like ⌥/Alt while dragging) or "copy"
the number of filled cells (batch edit: marked); -1 if more than maxCells
// rows 0 and 1 of column 2 hold 1 and 2: rows 2…9 get 3…10
tableApi.fillRange(new CellRange(0, 2, 1, 2), "down", 8);
1.1.96
Searches through all rows in the table's body model to find a row that matches specified criteria.
This method works only with AreaModelObjectArray
The type of objects/rows in the table
A partial object containing the search criteria. Only needs to include the properties you want to match against.
A function that defines how to match rows against the criteria. Returns true if the row matches the criteria, false otherwise.
The first matching row, or undefined if no match is found or if the body model is not AreaModelObjectArray.
// Find a user row by id and name
const criteria = { id: 1, name: "John" };
const user = tableApi.findRowFromAllRowsByAllCriteria(criteria,
(criteria, row) => row.id === criteria.id && row.name === criteria.name
);
// Find a product row with partial match on name
const criteria = { name: "Phone" };
const product = tableApi.findRowFromAllRowsByAllCriteria(criteria,
(criteria, row) => row.name.includes(criteria.name!)
);
// Find an order with complex matching logic
const criteria = { total: 100, status: "paid" as const };
const order = tableApi.findRowFromAllRowsByAllCriteria<Order>(criteria,
(criteria, row) => {
return row.total > criteria.total! &&
row.status === criteria.status;
}
);
Logs a warning to console if the body model is not an instance of AreaModelObjectArray
Searches through the filtered rows of the table to find a row that matches specific criteria. This method only works with tables that use AreaModelObjectArray as their body model.
The type of the row objects in the table
A partial object containing the search criteria. Only needs to include the properties you want to match against.
A function that defines how to match rows against the criteria. Returns true if the row matches the criteria, false otherwise.
The first matching row from the filtered rows, or undefined if no match is found or if the body model is not an instance of AreaModelObjectArray.
interface Person {
id: number;
name: string;
age: number;
}
// Find a person by exact match
const criteria = { name: "John", age: 30 };
const person = tableApi.findRowFromFilteredRowsByAllCriteria<Person>(
criteria,
(criteria, row) => row.name === criteria.name && row.age === criteria.age
);
// Find a person by partial name match
const searchCriteria = { name: "Jo" };
const person2 = tableApi.findRowFromFilteredRowsByAllCriteria<Person>(
searchCriteria,
(criteria, row) => row.name.toLowerCase().includes(criteria.name!.toLowerCase())
);
Logs a warning to console if the body model is not an instance of AreaModelObjectArray
Searches for and returns rows from the table body that match the given criteria.
Only works with an AreaModelObjectArray body – logs a console warning otherwise.
The type of elements in the rows array
An array of rows to search for in the table body
A comparison function that determines if two rows match
An array containing all matching rows found in the table body
This method searches through the table body model for rows that match the provided criteria. It only works with tables that use AreaModelObjectArray as their body model. The method uses a predicate function to determine matches between rows.
// Find rows with simple equality comparison
const matchingRows = tableApi.findRows([orders[0], orders[1]]);
// Find rows with custom comparison logic
const matchingRows = tableApi.findRows<Order>([{ ...orders[0] }], (a, b) => a.id === b.id);
The active body cell (the focused cell), undefined if there is none or the header has the focus.
display row and model column
const cell = tableApi.getActiveCell();
if (cell) console.log(tableApi.getCellAddress(cell[0], cell[1])); // "B3"
1.1.99
Retrieves the area model for the body section of the table.
The area model interface that provides access to the table's body data, including rows, cells, and their values.
The address of a body cell in cell formulas: "B3" = second data column, third row of getAllRows() (the
physical row – sorting and filtering don't change it). undefined without cell formulas or for a row number or
checkbox column.
display row
model column index or property
the address
tableApi.getCellAddress(1, "total"); // "D2" – the second row of getAllRows()
1.1.99
The body cell of an address ("B3", "$B$3"): display row (-1 if a filter hides it) and model column;
undefined without cell formulas or for an address outside the data.
the address
display row and model column
const cell = tableApi.getCellByAddress("D5");
if (cell && cell[0] >= 0) tableApi.scrollToIndex(cell[1], cell[0]);
1.1.99
The input of a body cell – what was typed: the formula text of a cell formula ("=B2*C2"), otherwise the value.
display row
model column index or property
the input
tableApi.getCellInput(1, "total"); // "=B2*C2"
1.1.99
The value of a body cell – the result of a cell formula (2598), otherwise the value (= getValueAt).
display row
model column index or property
the value
tableApi.getCellValue(1, "total"); // 2598
1.1.99
The index of the column that shows a property – -1 if there is none.
Column indices count all columns, also a RowNumberColumnDef or CheckboxColumnDef in front:
with row numbers (rowNumbersVisible: true) every data column has an index one higher. Get the index by the
property instead of writing numbers into your code.
the property of the column definition, e.g. "total"
the column index (fixed columns included) or -1
const totalColumn = tableApi.getColumnIndex("total");
tableApi.scrollToIndex(totalColumn, 0);
1.1.93
The cells whose formulas read a cell directly (display row, model column; rows a filter hides are left out).
display row
model column index or property
the dependent cells
tableApi.getDependents(1, "price"); // [[1, 3]] – D2 reads B2
1.1.99
The container of a detail row (the element your detailRenderer renders into) – only while its content exists
(it is created when the detail row becomes visible).
display index or row object
the container
const notes = tableApi.getDetailElement(order)?.querySelector("textarea");
1.1.97
The writing direction of the table – TableOptionsIf.direction with "auto" already decided (from the page
when the table was built).
In "rtl" the first column is on the right; positions of the API stay "from the start": scrollToPixel,
GeScrollEvent.scrollLeft and the scroll part of getState count from the right edge.
"ltr" or "rtl"
if (tableApi.getDirection() === "rtl") {
console.info("first column on the right");
}
1.1.98
Retrieves the number of rows currently displayed in the table.
The number of rows currently displayed in the table.
This method returns the count of rows that are currently visible in the table after applying any filtering, pagination, or other visibility constraints.
Retrieves the index of the first fully visible row in the table's viewport.
A row is considered "fully visible" when it's completely within the visible area of the table. This differs from the regular "visible" row, which might be partially visible at the edges of the viewport.
This method is useful for:
The index of the first fully visible row, or -1 if no rows are fully visible
// Get the first fully visible row index
const firstFullVisibleRow = tableApi.getFirstFullVisibleRowIndex();
// Check if a specific row is fully visible
const isRowFullyVisible = (rowIndex: number) => {
const firstFullVisible = tableApi.getFirstFullVisibleRowIndex();
const lastFullVisible = tableApi.getLastFullVisibleRowIndex();
return rowIndex >= firstFullVisible && rowIndex <= lastFullVisible;
};
Returns the index of the first visible row in the table's viewport.
This method retrieves the first visible row index from the table's scope, which is useful for virtual scrolling and viewport-related calculations. The index represents the topmost visible row in the current scroll position of the table.
This value is maintained internally by the table and updated during scrolling operations. It's particularly important for:
The index of the first visible row in the table's viewport. Returns -1 if no rows are visible or if the table is not yet rendered.
// Get the first visible row index
const tableApi = new TableApi(tableScope);
const firstVisibleIndex = tableApi.getFirstVisibleRowIndex();
// Use the index for scroll position calculations
if (firstVisibleIndex >= 0) {
// Perform operations with the first visible row
console.log(`First visible row index: ${firstVisibleIndex}`);
}
The header cell that has the keyboard focus. Arrow up in the first body row moves the focus into the header (Enter sorts or opens/closes a header group there); arrow down leaves it. The FocusModelIf holds the body focus only and is not changed by this.
header row index and column index, undefined if the focus is in the body
const headerFocus = tableApi.getHeaderFocus();
if (headerFocus) {
const [headerRow, column] = headerFocus;
console.log(`header cell ${headerRow}/${column} has the focus`);
}
1.1.91
Returns the index of the last fully visible row in the table's viewport.
This method retrieves the last row that is completely visible within the current viewport of the table. A row is considered "fully visible" when its entire height is visible without any portion being cut off by the viewport boundaries.
The distinction between "visible" and "fully visible" is important when scrolling:
The index of the last fully visible row in the table. If no row is fully visible or the table is empty, the method might return -1.
// Get the index of the last fully visible row
const lastVisibleRowIndex = tableApi.getLastFullVisibleRowIndex();
// Use this information to determine if a specific row is fully visible
const isRowFullyVisible = rowIndex <= lastVisibleRowIndex && rowIndex >= tableApi.getFirstFullVisibleRowIndex();
// Can be used with scrolling operations to ensure certain rows are visible
if (rowIndex > lastVisibleRowIndex) {
tableApi.ensureRowIsVisible(rowIndex);
}
Returns the index of the last row that is visible in the table's viewport.
This method retrieves the last visible row index from the table scope, which keeps track of which rows are currently visible as the user scrolls through the table.
This can be useful for:
The index of the last visible row in the table viewport
// Check if a specific row is currently visible in the viewport
const lastVisibleIndex = tableApi.getLastVisibleRowIndex();
const firstVisibleIndex = tableApi.getFirstVisibleRowIndex();
const isRowVisible = rowIndex >= firstVisibleIndex && rowIndex <= lastVisibleIndex;
// Get the range of visible rows
const visibleRowsRange = {
first: tableApi.getFirstVisibleRowIndex(),
last: tableApi.getLastVisibleRowIndex()
};
console.log(`Visible rows: ${visibleRowsRange.first} to ${visibleRowsRange.last}`);
The overlay shown now: "none", "loading", "noRows" or "noMatches". During
OverlayOptionsIf.loadingDelayInMs the loading overlay is requested but not shown yet – the automatic
overlay stays meanwhile (e.g. "noRows" of an empty table), so a fast answer causes no flicker.
the shown overlay
if (tableApi.getOverlayState() === "noMatches") {
resetFilterButton.hidden = false;
}
1.1.92
The values of a batch edit that are not written yet: row object, property, old and new value, the current display
index (-1 while a filter hides the row) and the message of an invalid value. The changes belong to the rows, not
to their positions – sorting and filtering don't mix them up.
the pending cells
saveButton.onclick = async () => {
const changes = tableApi.getPendingChanges()
.map(c => ({ id: (c.row as { id: number }).id, field: c.property, value: c.newValue }));
const response = await fetch("/api/prices", { method: "PATCH", body: JSON.stringify(changes) });
if (response.ok) tableApi.commitBatchEdit();
};
1.1.96
Pivot table: the current configuration.
a copy, undefined if the table model is no pivot table
const columnFields = tableApi.getPivotConfig()?.columnFields; // ["monat"]
1.1.94
The cells and ranges a cell formula reads, as addresses.
display row
model column index or property
e.g. ["B2", "C2"] (empty for a value cell or without cell formulas)
tableApi.getPrecedents(1, "total"); // ["B2", "C2"]
1.1.99
Row grouping: the current levels.
the levels, undefined if the body is no row grouping
const levels = tableApi.getRowGroupBy(); // ["region", "produkt"]
1.1.94
The value of a column for a row object – computed for a calculated column (valueGetter, formula), otherwise
the property (path). Use it in the external filter to filter by calculated values; a TreeRow is unwrapped.
a row object (or the TreeRow a tree filter gets)
the property of the column
the value (a FormulaError for a formula error)
tableOptions.externalFilterFunction = (row: Item) => Number(tableApi.getRowValue(row, "total")) > 500;
tableApi.externalFilterChanged();
1.1.95
Count, sum, average, minimum and maximum of the selected body cells – for a status bar like in a spreadsheet.
Counted are the selected cells of the visible data columns; the numbers are the cell values of type number.
Above SelectionAggregator.maxCells cells (default 1,000,000) nothing is computed (capped: true).
Without a selection all values are 0 / undefined.
{ count, numericCount, sum, avg, min, max, capped }
// a status bar in plain HTML under the table
const statusBar = document.querySelector<HTMLDivElement>("#status")!;
const listener = new EventAdapter();
listener.onSelectionChanged = () => {
const a = tableApi.getSelectionAggregates();
statusBar.textContent = a.capped ? "Selection too large"
: a.numericCount ? `Count ${a.count} · Sum ${formatAggregateValue(a.sum)} · Average ${formatAggregateValue(a.avg)}`
: "";
};
1.1.94
The selected cells as tab-separated text, exactly as Ctrl/⌘+C copies them (Excel's quoting, copyHeaders,
copyValue). "" without cells or with more than CopyService.maxCopyCells.
the text
console.log(tableApi.getSelectionAsText()); // "a\tb\n1\t2"
1.1.99
Returns the current selection model for the table.
The selection model manages cell selection state, including single cells, ranges, rows, and columns. This method provides access to the selection model instance, allowing operations like:
The current selection model if available, otherwise undefined.
// Get the selection model
const selectionModel = tableApi.getSelectionModel();
// Check if a specific cell is selected
if (selectionModel?.isSelected(3, 2)) {
console.log('Cell at row 3, column 2 is selected');
}
// Get all selected ranges
const ranges = selectionModel?.getRanges();
console.log('Selected ranges:', ranges);
// Clear all selections
selectionModel?.clear();
// Toggle selection of a cell
selectionModel?.togglePoint(5, 1);
// Add a selection change listener
selectionModel?.addEventSelectionChangedListener({
onSelectionChanged: () => {
console.log('Selection has changed');
}
});
The inputs of a spreadsheet (SpreadsheetFactory) as rows × columns – formulas as text, up to the last
filled row and column (empty cells ""). Save them; SpreadsheetFactory.create({ data }) loads them again.
undefined for other models (their data are the app's rows).
the data
localStorage.setItem("sheet", JSON.stringify(tableApi.getSheetData()));
1.1.99
The formula engine of the cell formulas (undefined without them) – physical rows, data columns.
the engine
tableApi.getSheetEngine()?.getFormulaCount(); // 4
1.1.99
Retrieves the shortcut action mapping from the table's shortcut service.
This method provides access to the keyboard shortcut configuration that defines which keys trigger specific actions in the table. The mapping contains key-value pairs where:
This mapping can be useful for:
An object mapping shortcut key combinations to their corresponding action IDs. For example: { "ctrl+c": "COPY_2_CLIPBOARD", "arrow_down": "NAVIGATE_DOWN" }
// Get all available shortcuts in the table
const shortcuts = tableApi.getShortcutActionMapping();
// Check if a specific shortcut exists
if (shortcuts["ctrl+c"]) {
console.log("Copy shortcut is available with action:", shortcuts["ctrl+c"]);
}
// Display available shortcuts in the UI
Object.entries(shortcuts).forEach(([key, actionId]) => {
console.log(`${key}: ${actionId}`);
});
„Show values as“ of a column.
the property of the column
the configuration, undefined for none
const mode = tableApi.getShowValuesAs("total"); // "percentOfColumnTotal"
1.1.95
The state of the table as plain JSON: column layout (order, widths, visibility), sorting, scroll position,
expanded tree nodes, checked and selected rows, header groups and your own data (setCustomState).
Columns are identified by property and rows by id (AutoRestoreOptionsIf.getRowId), never by index.
Parts that do not apply to this table are left out (e.g. tree for a flat table).
Store it wherever you like and give it back with applyState or TableOptionsIf.initialState;
TableOptionsIf.autoRestoreOptions does this for you in localStorage.
Optional parts: TableStatePart[]only these parts, default: all
the state
const state = tableApi.getState(); // { version: 1, columns: [...], sort: [...], ... }
await fetch("/api/prefs/orders", { method: "PUT", body: JSON.stringify(state) });
const layout = tableApi.getState(["columns", "sort"]); // only the layout, no scroll position or selection
Training: Remember the state
1.1.92
Retrieves the table model that contains all data and structure information for the table.
The table model interface that provides access to the table's data structure, including header, body, and footer area models.
Returns the TableScope instance associated with this TableApi.
The TableScope provides access to the core functionality and internal state of the table component, including rendering, event handling, selection, and other low-level operations.
This method is useful for advanced customization scenarios where you need direct access to the table's internal structure and behaviors.
The TableScope instance that controls this table component
// Access the TableScope to perform a custom operation
const tableApi = new TableApi(myTableScope);
const tableScope = tableApi.getTableScope();
// Example: Manually trigger a context menu at a specific location
const mouseEvent = new MouseEvent('contextmenu');
const geMouseEvent = tableScope.createGeMouseEvent(mouseEvent);
tableScope.contextmenu(geMouseEvent);
The state of the undo history (what onUndoStackChanged reports).
can undo/redo, labels, size; undefined without the option
const state = tableApi.getUndoState();
undoButton.title = state?.undoLabel ? `Undo ${state.undoLabel}` : "Undo";
1.1.99
Goes to a cell or range of the sheet by its address (cell formulas): "D10" makes it the active cell, "B2:C5"
selects the range (its first cell becomes active); the view scrolls to it. false for an invalid address, one
outside the data or a row a filter hides.
e.g. "D10", "$B$2:C5"
true if it went there
tableApi.goToAddress("B2:C5");
1.1.99
True if the table calculates cell formulas (TableOptionsIf.cellFormulas, flat body).
whether cell formulas are on
const showAddresses = tableApi.hasCellFormulas();
1.1.99
True if a batch edit has values that are not written yet.
whether there are pending changes
window.addEventListener("beforeunload", evt => {
if (tableApi.hasPendingChanges()) evt.preventDefault(); // "Leave site? Changes may not be saved."
});
1.1.96
Ends the overlay shown by showLoadingOverlay or showNoRowsOverlay.
"No rows to show" / "No matching rows" are not ended: they show the state of the body and stay as long as it
is empty (switch them off with overlayOptions: { autoNoRows: false }).
tableApi.showLoadingOverlay("Saving…");
save(order).finally(() => tableApi.hideOverlay());
1.1.92
Inserts empty columns before a column of a spreadsheet (SpreadsheetFactory; at = letter index, A = 0)
and adjusts the cell formulas. One undo step.
the data column the new columns come before
Optional count: number = 1columns
true if inserted (false for other models)
tableApi.insertColumns(1); // a new column B
1.1.99
Inserts empty rows before a row of the sheet (at = row number − 1, the physical row: also when sorted or
filtered) and adjusts the cell formulas (=SUM(D2:D4) grows when rows are inserted inside). One undo step, one
onModelChanged (source "structure"). Works for a spreadsheet (SpreadsheetFactory), object rows (new
objects from clipboard.createRow, otherwise {} – the app's array changes in place) and arrays.
the physical row the new rows come before (row count = append)
Optional count: number = 1rows
true if inserted
tableApi.insertRows(4, 2); // two empty rows before row 5
1.1.99
True while values are collected (after startBatchEdit, or always with TableOptionsIf.batchEdit).
whether a batch edit runs
saveButton.hidden = !tableApi.isBatchEditing();
1.1.96
Returns whether a column is visible (ColumnDefIf.isVisible, see setColumnVisible). Columns without a column definition are always visible.
column index, property or column definition
if (!tableApi.isColumnVisible("category")) {
tableApi.setColumnVisible("category", true);
}
Determines if the footer is visible: it has at least one row and is not hidden with setFooterVisible.
True if the footer is visible, false otherwise.
const footerShown = tableApi.isFooterVisible();
Checks if the header is visible: it has at least one row and is not hidden with setHeaderVisible.
const headerShown = tableApi.isHeaderVisible();
Checks whether logging is currently active for the table component.
This method returns the current state of logging for the table. When logging is active, the table outputs detailed information about its operations to the console, including:
Logging can be toggled using the setLoggingActive(boolean) method.
True if logging is currently enabled, false otherwise.
// Check if logging is enabled
if (tableApi.isLoggingActive()) {
console.log("Table logging is currently active");
}
// Conditionally enable logging only if it's not already active
if (!tableApi.isLoggingActive()) {
tableApi.setLoggingActive(true);
// Perform operations that need debugging
tableApi.updateCells([
{
area: "body",
rowIndex: 2,
columnIndex: 3,
value: "New Value",
cssClasses: { "highlight": true }
}
]);
// Run some additional operations...
// Disable logging when finished
tableApi.setLoggingActive(false);
}
Moves body rows – the code way to do what row drag does (e.g. "up"/"down" buttons, the pointer alternative of
WCAG 2.5.7, or after the server confirmed an unmanaged drag). The body model must support it
(AreaModelIf.moveRows: object arrays, arrays of arrays, trees); no filter may hide rows and no column may be
sorted (asc/desc – a sorted view has no order of its own). Focus and a multi-row selection follow the rows; screen
readers hear "Row moved to position 7 of 20". No row drag events. false if nothing moved (also for a target
where the rows already are).
display index or indices of the rows to move
drop target; a number = in front of that display index
(getDisplayedRowCount() = behind the last row)
true if the rows were moved
tableApi.moveRows(2, { rowIndex: 6, position: "after" }); // row 3 becomes row 7
tableApi.moveRows([0, 1], tableApi.getDisplayedRowCount()); // the first two rows to the end
// unmanaged drag: the server decides, then the table moves the rows
const listener = new EventAdapter();
listener.onRowDragEnd = async (evt: GeRowDragEvent) => {
if (evt.type === "end" && evt.target && (await fetch("/api/move", { method: "POST" })).ok) {
tableApi.moveRows(evt.rowIndices, evt.target);
}
};
1.1.95
Pastes tab-separated text like Ctrl/⌘+V – without clipboard permissions: from target (once), otherwise from the
selection (tiled over a selected range that is an integer multiple, one value into every selected cell) or the
focused cell. Hidden columns are skipped, read-only cells left out, the rest is cut off at the last column;
one onModelChanged with source: "paste" (batch edit: marked). Works without TableOptionsIf.clipboard.
rows separated by line breaks, cells by tabs (quoted cells like Excel)
Optional target: [number, number]display row and model column of the first cell
the written (batch edit: newly pending) cells
tableApi.pasteText("a\tb\n1\t2", [0, 1]); // A 2×2 block from row 0, column 1
1.1.99
Re-applies the current sorting configuration to the table data and repaints.
The current sorting is the last sort function, otherwise the columns with a sort state (the header icons,
also restored by autoRestoreOptions). Useful when the underlying data has changed and needs to be re-sorted
without changing the sort columns or direction. (Before 1.1.91 only the sort items stored by autoRestoreOptions
were used – without them reSort() did nothing.)
orders[0].total = 999_999;
tableApi.reSort();
Recalculates the dimensions of all wrapper elements in the table.
This method is typically called after changes to the table structure or content that might affect the layout, such as resizing columns or changing row heights. It ensures that all wrapper elements are properly sized to match their content.
Calculates all cell formulas again – after the app changed the data without the table (updateCells,
applyCellChanges and the editor recalculate by themselves; repaint does not). Also renews
TODAY(), NOW() and RAND().
rows[3].price = 30; // changed outside the table
tableApi.recalculate();
1.1.99
Destroys the content of an expanded detail row and creates it again (context.recreated is true) – e.g. after
the data it shows changed.
display index or row object
false if the row is not expanded
order.items.push(newItem);
tableApi.refreshDetail(order);
1.1.97
Removes specified rows from the table body.
Only works with an AreaModelObjectArray body – logs a console warning otherwise. Does not repaint and resets filtering and sorting of the body: afterwards call externalFilterChanged (if a filter function is set) and repaintHard (the row count changed).
The type of elements in the rows array
An array of data objects to remove from the table
A comparison function that determines if two rows match
This method removes rows from the table body that match any of the provided rows based on the predicate function. It only works with tables that use AreaModelObjectArray as their body model.
Repaints the table.
This method refreshes the visual representation of the table by delegating to the tableScope's repaint functionality. Use this method whenever the table's visual state needs to be updated after data changes, selection changes, or any modifications that affect the rendered output.
Repainting the table ensures that all visible cells, rows, and styling reflect the current state of the underlying data model and configuration settings.
Since 1.1.94 it also drops cached totals (AreaModelIf.invalidateDerivedValues, e.g. of a footer from
footerAggregates), so they are computed again from the changed rows. Scrolling never does that.
// Update data and repaint the table
const tableApi = new TableApi(tableScope);
tableApi.setRows(newData);
tableApi.repaint();
// Update selection and repaint to show the visual change
tableApi.getSelectionModel()?.togglePoint(2, 3);
tableApi.repaint();
// After changing values in the row objects yourself, repaint to show them
orders[0].status = "paid";
tableApi.repaint();
Performs a complete repaint of the table by triggering a hard repaint on the underlying table scope.
A hard repaint is more thorough than a regular repaint and performs the following operations:
Use this method when regular repaint isn't sufficient, such as when:
This method doesn't return a value
// Example 1: Basic usage
tableApi.repaintHard();
// Example 2: Using after major data changes
tableApi.setRows(newDataSet);
tableApi.repaintHard();
// Example 3: Using after resizing a container
window.addEventListener('resize', () => {
tableApi.repaintHard();
});
// Example 4: Using after changing ColumnDefIf.isVisible yourself
// (setColumnVisible() does this on its own)
column.isVisible = FalseFn;
tableApi.repaintHard();
Back to the state of the code: column layout, sort icons and header groups as they were right after the table was built (before a stored state was applied) – tree, checked and selected rows too when they are remembered (TableOptionsIf.autoRestoreOptions) or given as TableOptionsIf.initialState –, scrolled to the top, custom data removed. Deletes the stored state. The rows keep their current order (a table has no "unsorted" order to go back to); only the sort icons of the code come back.
document.querySelector("#reset-layout")!.addEventListener("click", () => tableApi.resetState());
1.1.92
Scrolls the table to display the specified row and column indices at the top-left corner of the viewport.
The row becomes the first row of the body, the column the first column of the scrollable (center) part – as far as the scroll range allows: the last columns and rows cannot move further than to the right/bottom edge.
Fixed columns (fixedLeftColumnCount / fixedRightColumnCount) are always visible: a fixed west column scrolls
back to the start (x = 0), a fixed east column – and -1 or any other index outside the columns – keeps the
current horizontal position.
The column index (model index, fixed columns included) to scroll to.
Defaults to 0 (first column). -1 keeps the horizontal position.
The vertical index (row) to scroll to. Defaults to 0 (topmost row).
// Scroll to the 5th row, keeping the current horizontal scroll position
tableApi.scrollToIndex(-1, 5);
// Scroll to column 3, row 10
tableApi.scrollToIndex(3, 10);
// Scroll to column 3, keeping the rows that are currently shown
tableApi.scrollToIndex(3, tableApi.getFirstVisibleRowIndex());
Scrolls the table content to the specified pixel coordinates.
This method allows programmatic scrolling of the table viewport to exact pixel positions. It delegates the scrolling operation to the underlying TableScope instance.
The horizontal pixel coordinate to scroll to: px from the start of the row – 0 = the first column (leftmost position; right-to-left the rightmost, see TableOptionsIf.direction). Defaults to 0.
The vertical pixel coordinate to scroll to. Defaults to 0 (topmost position).
// Scroll to the top-left corner of the table
tableApi.scrollToPixel(0, 0);
// Scroll 200 pixels horizontally and 150 pixels vertically
tableApi.scrollToPixel(200, 150);
// Scroll only vertically, keeping the current horizontal position
// (an omitted or undefined px means 0; for rows use scrollToIndex(-1, rowIndex))
tableApi.scrollToPixel(tableApi.getTableScope().getViewportScrollLeft(), 300);
// Example in a component that needs precise scrolling control
class TableScroller {
private tableApi: TableApi;
constructor(tableApi: TableApi) {
this.tableApi = tableApi;
}
scrollToPosition(x: number, y: number) {
// Scroll to exact pixel coordinates
this.tableApi.scrollToPixel(x, y);
}
scrollHalfway() {
const tableModel = this.tableApi.getTableModel();
const totalWidth = tableModel.getContentWidthInPixel();
const totalHeight = tableModel.getContentHeightInPixel();
// Scroll to the middle of the table content
this.tableApi.scrollToPixel(totalWidth / 2, totalHeight / 2);
}
}
Writes the input of a body cell (a value or a formula "=B2*C2") like the editor: one applyCellChanges.
display row
model column index or property
the input
true if written (or marked in a batch edit)
tableApi.setCellInput(4, "total", "=SUM(D2:D4)");
1.1.99
Sets (or replaces) the formula of a column – e.g. from an input field of your app. The formula is compiled
once; an invalid formula is set anyway (the cells show #ERROR! / #NAME?) and its error is returned, so you can
show the message next to the input. No code is ever executed (no eval). The table repaints; totals, sorting
and filter see the new values after repaint, reSort() resp. externalFilterChanged().
the property of the column (it becomes a calculated column)
the formula, e.g. "[price] * [quantity] * 1.19"
Optional options: FormulaOptionsIfown functions
the error of the formula itself, undefined if it is valid
(#NAME? also if there is no such column)
input.addEventListener("change", () => {
const error = tableApi.setColumnFormula("custom", input.value);
hint.textContent = error ? `${error.code} ${error.message}` : "";
});
1.1.95
Shows or hides a column and repaints the table.
The column keeps its definition, its position (also when other columns are dragged) and its width: a hidden column is just not rendered (width 0), not copied (copyToClipboard) and not exported (downloadExcel). Showing it again restores the previous width. Works for fixed columns, too.
The same as ColumnDefIf.isVisible – this method sets it to TrueFn / FalseFn (an own
isVisible function of the column is replaced). Columns without a column definition (e.g. bodyData +
columnSizes without columnDefs) can't be hidden.
column index (fixed columns included), property (e.g. "category")
or the column definition
Optional visible: boolean = truefalse hides the column
false if the column (definition) does not exist
// hide a column by property, show it again later
tableApi.setColumnVisible("category", false);
tableApi.setColumnVisible("category", true);
// a checkbox list "visible columns"
tableApi.getTableModel().getColumnDefs()?.forEach((columnDef, index) => {
const checkbox = document.createElement("input");
checkbox.type = "checkbox";
checkbox.checked = tableApi.isColumnVisible(index);
checkbox.addEventListener("change", () => tableApi.setColumnVisible(columnDef, checkbox.checked));
document.body.append(checkbox, columnDef.headerLabel);
});
Stores data of your application with the table state (part custom), e.g. the text of a filter field. It is
reported with EventListenerIf.onStateChanged, stored with autoRestoreCustomState and returned by
getState – also after a reload, so you can restore your filter in tableReady. Must be JSON.
your data (JSON)
filterInput.addEventListener("input", () => {
tableApi.setCustomState({ filter: filterInput.value });
tableApi.externalFilterChanged();
});
// after a reload (tableReady):
const custom = tableApi.getState(["custom"]).custom as { filter?: string } | undefined;
filterInput.value = custom?.filter ?? "";
1.1.92
Expands or collapses the detail row of a master row (master/detail, TableOptionsIf.masterDetail). The
content is created when the detail row becomes visible; collapsing destroys it. Reports
EventListenerIf.onDetailExpandedChanged (source "api").
display index or row object
true = expand, false = collapse
false if nothing changed (no master row, already in that state, no master/detail)
tableApi.setDetailExpanded(0, true); // the first shown row
tableApi.setDetailExpanded(orders[42], true); // a row object (also if it is filtered out)
1.1.97
Shows or hides the footer and repaints the table.
A hidden footer takes no space – the body gets taller. Its rows stay in the model, so setFooterVisible(true)
brings them back (an own footer model, e.g. totals, is asked for its values again).
Requires a TableModel (what all factories create).
Optional visible: boolean = truefalse hides the footer
// show the totals footer only while a filter is active
tableApi.setFooterVisible(!!tableOptions.externalFilterFunction);
Collapses or expands a header group (CellGroupIf with toggle: true) – like a click on its toggle.
CellGroupIf.id, otherwise the path of the labels, e.g. "Customer" or "2026/Q1"
true = collapse
false if the header has no such group
tableApi.setHeaderGroupCollapsed("Customer", true);
1.1.92
Shows or hides the header and repaints the table.
A hidden header takes no space – the body moves up and shows more rows. The header rows stay in the model
(labels, sort icons, header groups), so setHeaderVisible(true) brings them back unchanged.
Requires a TableModel (what all factories create).
Optional visible: boolean = truefalse hides the header
// a compact view without column headers
tableApi.setHeaderVisible(false);
// toggle from a button
document.querySelector("#toggle-header")!.addEventListener("click", () =>
tableApi.setHeaderVisible(!tableApi.isHeaderVisible()));
Checks whether logging is currently active for the table component.
This method returns the current state of logging for the table. When logging is active, the table outputs detailed information about its operations to the console, including:
Logging can be toggled using the setLoggingActive(boolean) method.
True if logging is currently enabled, false otherwise.
// Check if logging is enabled
if (tableApi.isLoggingActive()) {
console.log("Table logging is currently active");
}
// Conditionally enable logging only if it's not already active
if (!tableApi.isLoggingActive()) {
tableApi.setLoggingActive(true);
// Perform operations that need debugging
tableApi.updateCells([
{
area: "body",
rowIndex: 2,
columnIndex: 3,
value: "New Value",
cssClasses: { "highlight": true }
}
]);
// Run some additional operations...
// Disable logging when finished
tableApi.setLoggingActive(false);
}
Pivot table (PivotFactory): pivots again – other row fields, column fields or values, or new rows –
without a new table model (a new model would rebuild the table in every framework wrapper). The model gets new
columns, header and totals; rows whose path stays keep their expanded state, the filter stays, column widths of
columns that stay are kept, so are columns hidden with setColumnVisible. Sorting, selection and focus are reset
(the columns are different). An invalid configuration throws and changes nothing.
the changed parts, e.g. { columnFields: ["jahr"] }; {} = pivot the
current rows again (e.g. after new keys)
Optional rows: any[]new source rows (default: the current ones)
false if the table model is no pivot table
// a select with the column field
select.addEventListener("change", () => tableApi.setPivotConfig({ columnFields: [select.value] }));
tableApi.setPivotConfig({ values: [{ value: "umsatz", aggregator: "avg" }] }); // averages instead of sums
tableApi.setPivotConfig({}, await loadSales()); // new data
1.1.94
Tree tables: expands or collapses the displayed body row rowIndex (like a click on its arrow), repaints and
remembers the state with autoRestoreOptions.
index of the displayed body row
the new state
false if the table is no tree, the row does not exist or already has this state
tableApi.setRowExpanded(0, true); // expand the first row
Row grouping (RowGroupFactory): groups the rows by other levels – without a new table model (a new model would rebuild the table in every framework wrapper). Groups whose path stays keep their expanded state; filter, sorting and checked rows stay; the columns of the grouped properties are hidden (the previously grouped ones come back) and the header of the group column follows. An empty list shows the rows without groups.
the new levels, top first: property paths, functions or RowGroupFieldIf
false if the body is no row grouping
// a select with "region", "produkt", "region,produkt"
select.addEventListener("change", () => tableApi.setRowGroupBy(select.value.split(",")));
tableApi.setRowGroupBy([]); // flat again (with the total row)
1.1.94
Sets the rows data for the table body, replacing any existing rows.
Only works with an AreaModelObjectArray body – logs a console warning otherwise. Does not repaint and resets filtering and sorting of the body: afterwards call externalFilterChanged (if a filter function is set) and repaintHard (the row count changed).
The type of elements in the rows array
An array of data objects to set as the table's rows
This method replaces all existing rows in the table body with the provided array of data objects. It only works with tables that use AreaModelObjectArray as their body model.
Sets the selection model for the table.
This method allows you to replace the current selection model with a new one, which controls how cells, rows, or columns can be selected in the table.
The new selection model to be set. This model defines the selection behavior including selection type (none, cell, row, column), selection mode (single, multi), and manages the selected ranges.
Optional repaint: boolean = trueWhether to repaint the table after changing the selection model. Default is true, which immediately reflects the change visually. Set to false if you want to avoid immediate repainting.
// Create a new selection model with row selection and multi-select mode
const selectionModel = new SelectionModel("row", "multi");
// Set the new selection model and repaint the table
tableApi.setSelectionModel(selectionModel);
// Set a selection model without repainting (if you plan to make more changes)
tableApi.setSelectionModel(selectionModel, false);
// ... make other changes ...
tableApi.repaint(); // Now repaint once
„Show values as“: shows the numbers of a column differently – share of the column total, running total, rank …
(see ShowValuesAs) – or normal again (undefined). No new model: the table repaints.
the property of the column
the mode (with options) or undefined for the values
false if there is no such column or the mode is unknown
select.addEventListener("change", () =>
tableApi.setShowValuesAs("total", select.value ? select.value as ShowValuesAs : undefined));
1.1.95
Shows "Loading…" with a CSS spinner over the body – e.g. while a server answers. The header and footer stay
readable; rows already shown stay visible under a semi-transparent layer. By default mouse and editing on the
body are blocked until hideOverlay (OverlayOptionsIf.blockInteractionWhileLoading).
Screen readers: the host gets aria-busy="true", the text is announced.
Texts, delay and own content: TableOptionsIf.overlayOptions. Framework wrappers have a loading prop
that calls this method (no re-creation of the table).
Optional text: stringtext for this overlay only, e.g. "Saving…"; default OverlayOptionsIf.loadingText
tableApi.showLoadingOverlay();
fetch("/api/orders")
.then(response => response.json())
.then((orders: object[]) => {
tableApi.setRows(orders);
tableApi.repaintHard();
})
.finally(() => tableApi.hideOverlay()); // 0 rows? "No rows to show" appears by itself
1.1.92
Training: Live data
Shows the "no rows" overlay, also over rows – e.g. when the server says "no permission". An empty body shows "No rows to show" anyway (OverlayOptionsIf.autoNoRows); this method is for other cases. Ends with hideOverlay.
Optional text: stringtext for this overlay only; default OverlayOptionsIf.noRowsText
tableApi.showNoRowsOverlay("You may not see these orders");
1.1.92
Sorts the table data using a custom comparison function.
This method allows sorting of table rows by providing a custom comparison function. The comparison function should follow the standard JavaScript array sort comparator pattern, returning:
A function that defines the sort order. The function should accept two arguments and return a number indicating their relative order: - Negative number: first argument should come before second - Positive number: second argument should come before first - Zero: elements are equal
// Sort table rows by a specific property in ascending order
tableApi.sort((a: Person, b: Person) => {
if (a.name < b.name) return -1;
if (a.name > b.name) return 1;
return 0;
});
// Sort numbers in descending order
tableApi.sort((a: { value: number }, b: { value: number }) => b.value - a.value);
// Sort with multiple criteria
tableApi.sort((a: Order, b: Order) => {
// First sort by status
const statusCompare = a.status.localeCompare(b.status);
if (statusCompare !== 0) return statusCompare;
// Then sort by name if status is equal
return a.customer.localeCompare(b.customer);
});
// Case-insensitive string sorting
tableApi.sort((a: Person, b: Person) =>
a.name.toLowerCase().localeCompare(b.name.toLowerCase())
);
// Tree tables: the function compares the tree rows of siblings (your objects: a.data / b.data),
// children stay below their parent
tableApi.sort((a: TreeRowIf<OrderNode>, b: TreeRowIf<OrderNode>) => b.data.total - a.data.total);
Sorts by one column from code – e.g. from your own button – exactly like a double click on the column header: rows, header icon, filter (sort order survives filtering) and auto-restore.
column index or property, e.g. "total"
Optional sortState: SortState"asc" or "desc"; omitted: toggles like the header (asc → desc → …)
false if the column does not exist
document.querySelector("#by-total")!.addEventListener("click", () => tableApi.sortByColumn("total", "desc"));
Starts a batch edit: from now on edited and filled values are collected – shown in the cells (class
ge-table-cell-pending), but not written into your rows – until commitBatchEdit or
cancelBatchEdit. Sorting, filtering, totals, copy and Excel use the written data. For a batch edit that is
always on use TableOptionsIf.batchEdit.
false if a batch edit is already running
editButton.onclick = () => tableApi.startBatchEdit();
saveButton.onclick = () => tableApi.commitBatchEdit();
discardButton.onclick = () => tableApi.cancelBatchEdit();
1.1.96
Opens the cell editor of a body cell – like F2 (caret at the end) or, with typedText, like typing that text.
With Excel-style editing (TableOptionsIf.editing) the editor sits above the cell; otherwise (or for
columns with an own getEditRenderer) it is rendered into the cell.
display row index of the body
model column index or property
Optional typedText: stringstarts in "enter" mode with this text (it replaces the content)
true if an editor is open (false for a cell that cannot be edited)
tableApi.startEditing(0, "price"); // edit the price of the first row
tableApi.startEditing(4, "name", "Ad"); // as if "Ad" had been typed
1.1.99
Closes an open cell editor: commits its input (like Enter without moving) or, with cancel, discards it (like
Escape).
Optional cancel: boolean = falsediscard the input
true if no editor is open afterwards (an invalid input keeps the editor open)
saveButton.addEventListener("click", () => {
tableApi.stopEditing(); // the value being typed belongs to the data that is saved
});
1.1.99
Adds an event listener next to the one the table was created with – e.g. for a toolbar or an own formula bar
that lives outside the component that created the table. Every on… method you give is called after the main
listener's; a listener that throws is logged and does not stop the others.
the events of interest
call it to remove the listener (the table also removes all on destroy())
Adds an event listener next to the one the table was created with – e.g. for a toolbar or an own formula bar
that lives outside the component that created the table. Every on… method you give is called after the main
listener's; a listener that throws is logged and does not stop the others.
call it to remove the listener (the table also removes all on destroy())
const unsubscribe = tableApi.subscribe({
onEditingChanged: (evt: GeEditEvent) => { hint.textContent = evt.type === "input" ? evt.input ?? "" : ""; },
onModelChanged: (evt: GeModelChangeEvent) => console.log(`${evt.changes?.length ?? 0} cells changed`),
});
// later
unsubscribe();
1.1.99
const unsubscribe = tableApi.subscribe({
onEditingChanged: (evt: GeEditEvent) => { hint.textContent = evt.type === "input" ? evt.input ?? "" : ""; },
onModelChanged: (evt: GeModelChangeEvent) => console.log(`${evt.changes?.length ?? 0} cells changed`),
});
// later
unsubscribe();
1.1.99
Expands a collapsed or collapses an expanded detail row (master/detail).
display index or row object
false if it is no master row (or no master/detail)
tableApi.toggleDetail(3); // e.g. from a button outside the table
1.1.97
Triggers the action with the given action ID. This function can be invoked programmatically.
The ID of the action to trigger.
Undoes the last change like Ctrl/⌘+Z (TableOptionsIf.undo): the old values are written back
(onModelChanged with source "undo"), the cells are selected and scrolled into view. Not while the cell editor
is open (it has its own text undo).
true if a step was undone; false without the option or with nothing to undo
undoButton.addEventListener("click", () => tableApi.undo());
1.1.99
Updates the cells in the table with the provided cell update events.
This method allows you to update specific cells in the table without requiring a full table repaint, which can significantly improve performance when updating large tables frequently.
An array of cell update events, each defining a single cell update operation. Each event must specify: - area: The table area to update ('header', 'body', or 'footer') - rowIndex: The row index of the cell to update - columnIndex: The column index of the cell to update - value: The new value to set for the cell - cssClasses: Optional object mapping CSS class names to boolean values (true to add the class, false to remove it)
Optional repaintAll: boolean = falseWhether to repaint the entire table after updating cells. - If true: All cells will be repainted after updating (slower but ensures visual consistency) - If false: Only the modified cells will be repainted (faster, recommended for frequent updates)
// Update a single cell with a new value and CSS classes
tableApi.updateCells([{
area: 'body',
rowIndex: 3,
columnIndex: 2,
value: 42,
cssClasses: {
'highlight': true, // Add 'highlight' class
'error': false // Remove 'error' class if present
}
}]);
// Update multiple cells at once
tableApi.updateCells([
{
area: 'body',
rowIndex: 1,
columnIndex: 1,
value: 'New Value 1',
cssClasses: { 'updated': true }
},
{
area: 'body',
rowIndex: 2,
columnIndex: 3,
value: 'New Value 2',
cssClasses: { 'updated': true }
}
]);
// Update cells and repaint the entire table
tableApi.updateCells([
{ area: 'body', rowIndex: 0, columnIndex: 0, value: 'Hello', cssClasses: { 'bg-green-0': true } },
{ area: 'body', rowIndex: 1, columnIndex: 1, value: 'World', cssClasses: { 'bg-red-0': true } }
], true);
// High-performance animation example (update without full repaint)
function animateCell() {
const value = Math.sin(Date.now() / 1000) * 50;
const cssClasses = {
'positive': value > 0,
'negative': value < 0
};
tableApi.updateCells([{
area: 'body',
rowIndex: 0,
columnIndex: 0,
value: Math.round(value),
cssClasses
}]);
requestAnimationFrame(animateCell);
}
animateCell();
Updates existing rows in the table body with new data.
Only works with an AreaModelObjectArray body – logs a console warning otherwise. The properties are copied into the existing objects; nothing is repainted – call repaint afterwards.
The type of elements in the rows array
An array of data objects containing the updated values
A comparison function that determines which rows to update
This method updates existing rows in the table body with new values from the provided rows array. For each row in the table, if the predicate returns true when compared with any row in the provided array, all properties from the provided row will be copied to the existing row. It only works with tables that use AreaModelObjectArray as their body model.
The public API of one table – everything you do with a table after it has been created: scrolling, sorting, filtering, selection, copy, Excel export, updating cells and rows, repainting.
Every table has exactly one
TableApi. You never create it yourself – you get it:new TableScope(host, model, new SimpleDomService(), options, listener).firstInit().getApi()el.addEventListener("tableReady", e => e.detail)<GuiexpertTable tableReady={api => …} /><GuiexpertTable @tableReady="api => …" /><GuiexpertTable onTableReady={api => …} /><guiexpert-table (tableReady)="onReady($event)" />The most important methods
Example
Example
Example
See
Training: Your first table, Sorting, Filtering, Excel export, Live data