Class AreaModelRowGroups<T>

The body of a row grouping: flat rows grouped by one or more levels, each group a row with an arrow, a label ("Nord (3)") and aggregates per column (sum, average, …); below it the sub-groups or the original rows. Usually created by RowGroupFactory.createTableModel, which also adds the group column, hides the grouped columns and adds a total row.

It is a tree (AreaModelTree, isAreaModelTree is true): group rows are tree rows with a RowGroupIf as data, detail rows hold your objects. Expanding, tableApi.expandAll(), keyboard, screen reader attributes and remembering expanded groups (autoRestoreOptions) work as in a tree table.

  • Filter: externalFilterFunction gets your object (as in a flat table) – never a group. Groups without a match disappear; aggregates and counts follow the filter.
  • Sorting: by an aggregated column → groups by aggregate, rows by value; by the group column → groups by key; tableApi.sort(fn) sorts the rows inside their groups with fn(a, b) on your objects.
  • Editing: group rows are not editable. A changed value recomputes only the groups of that row; a changed grouped value moves the row to its new group.
  • Regrouping: TableApi.setRowGroupBy (or setGroupBy + tableApi.repaintHard()) – expanded groups, filter, sorting and checked rows stay.
  • Aggregates are computed when a group row is read after a change – once, not per repaint or scroll frame.

Example

const tableModel = RowGroupFactory.createTableModel<Sale>({
rows: sales, columnDefs, groupBy: ["region", "produkt"], aggregations: { umsatz: "sum" }
});
const body = tableModel.getBodyModel() as AreaModelRowGroups<Sale>;
body.getGroups().map(g => g.results.umsatz); // [270, 180]

See

Training: Row grouping

Since

1.1.94

Type Parameters

  • T = any

    the row type

Hierarchy

Constructors

Properties

areaIdent: AreaIdent

The identifier for one of the areas: header, body, footer. This property determines which area of the table this model represents.

Example

// Creating an area model for the header
const headerModel = new AreaModel();
headerModel.areaIdent = "header";

Example

// Checking which area a model represents
if (areaModel.areaIdent === "body") {
// This is a body area model
}
cellRenderers: (undefined | CellRendererIf)[]
columnDefs: ColumnDefIf[] = []
defaultRowHeight: number
kind: "AreaModelRowGroups" = "AreaModelRowGroups"

Marker for isAreaModelRowGroups (works across copies of the core). type stays "AreaModelTree".

The options (groupBy as set by setGroupBy).

properties: string[]
rowSelectionModel: undefined | CheckboxModelIf<any> = undefined

A model and controller for row checkbox selection. This property allows the area to support row selection via checkboxes.

Example

// Creating a checkbox model for row selection
import { CheckboxModel } from "@guiexpert/table";

const bodyModel = new AreaModel();
bodyModel.areaIdent = "body";
bodyModel.rowSelectionModel = new CheckboxModel();

// Later, check if a row is selected
const isSelected = bodyModel.isRowChecked(5);
rows: TreeRowIf<any>[]
type: string = 'AreaModelTree'
yPositions: number[] = []

Methods

  • Runs several setValue calls as one change: a row whose grouped value changes moves only at the end, so the row indices of the other calls stay valid (tableApi.updateCells() uses it).

    Parameters

    • fn: (() => void)

      the changes

        • (): void
        • Returns void

    Returns boolean

    true if the groups were built again at the end

    Example

    const moved = body.batch(() => {
    body.setValue(1, 1, "Süd"); // region – moves the row
    body.setValue(2, 4, 999); // still the row that was at index 2
    });
  • Changes the order of columns by moving a column from one position to another. This method is called when the user drags a column to a new position.

    Parameters

    • sourceColumnIndex: number

      The index of the column to move

    • targetColumnIndex: number

      The index where the column should be moved to

    Returns void

    Example

    // Move the second column (index 1) to the fourth position (index 3)
    areaModel.changeColumnOrder(1, 3);

    Example

    // Implementation example
    changeColumnOrder(sourceColumnIndex: number, targetColumnIndex: number): void {
    // Move the column in the internal data structure
    this.arrayMove(this.columns, sourceColumnIndex, targetColumnIndex);

    // Update any dependent data structures
    this.arrayMove(this.cellRenderers, sourceColumnIndex, targetColumnIndex);
    }
  • Expands the groups above a level and collapses the others: 0 collapses all, 1 opens the top level, a large number opens all. Repaint with tableApi.repaintHard().

    Parameters

    • level: number

      the number of open levels

    Returns void

    Example

    body.expandToLevel(1);
    tableApi.repaintHard();
  • Filters the rows: predictFn gets your objects (as in a flat table), index and array of the source rows – never a group. Groups without a matching row disappear (also below collapsed groups); counts and aggregates follow the filter. Usually called by tableApi.externalFilterChanged() with tableOptions.externalFilterFunction.

    Type Parameters

    • S

    Parameters

    • predictFn: FilterFunction<S>

      returns a truthy value for the rows to show

    Returns void

    Example

    tableOptions.externalFilterFunction = (sale: Sale) => sale.monat === "Jan";
    tableApi.externalFilterChanged(); // Nord (2) 150 · Süd (1) 80
  • The rows of column aggregates in formulas (SUM([total])): the filtered row objects.

    Returns {
        length: number;
        rows: (() => ArrayLike<unknown>);
        signal: unknown;
    }

    • length: number
    • rows: (() => ArrayLike<unknown>)
        • (): ArrayLike<unknown>
        • Returns ArrayLike<unknown>

    • signal: unknown
  • Counts how often the groups were built (start, setRows, setGroupBy, regroup, an edited grouped value). The table compares it before and after an edit to know that the rows moved (→ hard repaint).

    Returns number

    the number of builds

    Example

    const before = body.getBuildCount();
    body.setValue(1, 1, "Ost");
    const moved = body.getBuildCount() !== before; // true: "region" is grouped
  • Returns the column span value for the cell at the specified row and column indices. A colspan value of N means the cell spans N columns horizontally. Values less than 2 are ignored (no spanning).

    Parameters

    • _rowIndex: number

      The index of the row

    • _columnIndex: number

      The index of the column

    Returns number

    The colspan value (number of columns the cell spans)

    Example

    // Check if a cell spans multiple columns
    const colspan = headerAreaModel.getColspanAt(0, 0);
    if (colspan > 1) {
    console.log(`This header cell spans ${colspan} columns`);
    }

    Example

    // Implementation example for a model with column spanning
    getColspanAt(rowIndex: number, columnIndex: number): number {
    // Special case: first row, first column spans 3 columns
    if (rowIndex === 0 && columnIndex === 0) return 3;

    // No spanning for other cells
    return 1;
    }
  • Returns an array of CSS class names to apply to the cell at the specified row and column indices. This method allows for custom styling of individual cells.

    Parameters

    • rowIndex: number

      The index of the row

    • columnIndex: number

      The index of the column

    Returns string[]

    An array of CSS class names

    Example

    // Apply custom classes to cells
    const classes = bodyAreaModel.getCustomClassesAt(1, 2);
    console.log(`Cell classes: ${classes.join(', ')}`);

    Example

    // Implementation example with conditional styling
    getCustomClassesAt(rowIndex: number, columnIndex: number): string[] {
    const classes: string[] = [];

    // Add classes based on row index
    if (rowIndex % 2 === 0) {
    classes.push('even-row');
    } else {
    classes.push('odd-row');
    }

    // Add classes based on cell value
    const value = this.getValueAt(rowIndex, columnIndex);
    if (typeof value === 'number' && value < 0) {
    classes.push('negative-value');
    }

    // Add classes for selected rows
    if (this.isRowChecked(rowIndex)) {
    classes.push('selected-row');
    }

    return classes;
    }
  • Returns an object containing CSS style properties to apply to the cell at the specified row and column indices. This method allows for inline styling of individual cells.

    Parameters

    • _rowIndex: number

      The index of the row

    • _columnIndex: number

      The index of the column

    Returns undefined | {
        [key: string]: string;
    }

    An object with CSS style properties, or undefined if no custom styles

    Example

    // Apply custom styles to cells
    const styles = bodyAreaModel.getCustomStyleAt(1, 2);
    console.log('Cell styles:', styles);

    Example

    // Implementation example with conditional styling
    getCustomStyleAt(rowIndex: number, columnIndex: number): { [key: string]: string } | undefined {
    // Apply background color based on value
    const value = this.getValueAt(rowIndex, columnIndex);

    if (typeof value === 'number') {
    // Color gradient based on value
    if (value < 0) {
    return { 'background-color': `rgba(255, 0, 0, ${Math.min(Math.abs(value) / 100, 0.5)})` };
    } else if (value > 0) {
    return { 'background-color': `rgba(0, 255, 0, ${Math.min(value / 100, 0.5)})` };
    }
    }

    return undefined; // No custom styles
    }
  • The displayed children of a row (in display order): all children, with an active filter only those that match (or have a matching descendant). They are on screen if the row is expanded. Used for the screen reader attributes aria-setsize / aria-posinset of tree rows.

    Parameters

    Returns TreeRowIf<any>[]

    the displayed children (empty for a leaf)

    Example

    const treeModel = tableModel.getBodyModel() as AreaModelTree<OrderNode>;
    const shown = treeModel.getDisplayedChildren(treeModel.rows[0]).length;

    Since

    1.1.91

  • The leaves (rows without children) of the whole tree, depth-first in the current order – independent of the expanded state. Used for totals: collapsing a node does not change them, and parent rows are not counted twice.

    Parameters

    • Optional filtered: boolean = true

      only the leaves the active filter keeps (externalFilterChanged); false: all

    Returns TreeRowIf<any>[]

    a new array

    Example

    const treeModel = tableModel.getBodyModel() as AreaModelTree<OrderNode>;
    const total = aggregate(treeModel.getLeafRows(), row => row.data.total, "sum");

    Since

    1.1.94

  • Returns the maximum colspan value that can occur in this area model. This value is used to optimize rendering performance.

    Returns number

    The maximum possible colspan value in this area

    Example

    // Get the maximum colspan for optimization
    const maxColspan = headerAreaModel.getMaxColspan();

    Example

    // Implementation example
    getMaxColspan(): number {
    // If the model has no colspans, return 0 for optimization
    return 0;

    // If the model has colspans, return the maximum possible value
    // return 3; // If some cells span up to 3 columns
    }
  • Returns the maximum rowspan value that can occur in this area model. This value is used to optimize rendering performance.

    Returns number

    The maximum possible rowspan value in this area

    Example

    // Get the maximum rowspan for optimization
    const maxRowspan = bodyAreaModel.getMaxRowspan();

    Example

    // Implementation example
    getMaxRowspan(): number {
    // If the model has no rowspans, return 0 for optimization
    return 0;

    // If the model has rowspans, return the maximum possible value
    // return 2; // If some cells span up to 2 rows
    }
  • Returns the row object at the specified index. This method provides access to the underlying data structure.

    Parameters

    • idx: number

      The index of the row

    Returns undefined | TreeRowIf<any>

    The row object

    Example

    // Get a row object
    const row = bodyAreaModel.getRowByIndex(1);
    console.log('Row data:', row);

    Example

    // Implementation example for an array-based model
    getRowByIndex(rowIndex: number): any {
    if (rowIndex < 0 || rowIndex >= this.data.length) return undefined;
    return this.data[rowIndex];
    }
  • Returns the number of rows in this area. This is a fundamental method that determines the vertical size of the area.

    Returns number

    The number of rows in the area

    Example

    // Get the number of rows in the body area
    const rowCount = bodyAreaModel.getRowCount();
    console.log(`The body has ${rowCount} rows`);

    Example

    // Implementation example for an array-based model
    getRowCount(): number {
    return this.data.length;
    }
  • Returns the height of the row at the specified index, in pixels. This method allows for variable row heights in the table.

    Parameters

    • _rowIndex: number

      The index of the row

    Returns number

    The height of the row in pixels

    Example

    // Get the height of a row
    const height = bodyAreaModel.getRowHeight(1);
    console.log(`Row 1 height: ${height}px`);

    Example

    // Implementation example with fixed row height
    getRowHeight(rowIndex: number): number {
    return this.defaultRowHeight; // e.g., 30px
    }

    Example

    // Implementation example with variable row heights
    getRowHeight(rowIndex: number): number {
    // First row is taller
    if (rowIndex === 0) return 50;

    // Rows with more content are taller
    const row = this.getRowByIndex(rowIndex);
    if (row && row.hasLongContent) return 60;

    // Default height for other rows
    return 30;
    }
  • The value of a column for a row object – the computed value of a calculated column, otherwise the property (path). A TreeRow is unwrapped. The table API offers it as TableApi.getRowValue.

    Parameters

    • row: any

      a row object (or TreeRow)

    • property: string

      the property of the column

    Returns unknown

    the value

    Example

    const body = tableModel.getBodyModel() as AreaModelObjectArray<Item>;
    body.getRowValue({ price: 25, quantity: 10 }, "total"); // 250

    Since

    1.1.95

  • Returns the row span value for the cell at the specified row and column indices. A rowspan value of N means the cell spans N rows vertically. Values less than 2 are ignored (no spanning).

    Parameters

    • _rowIndex: number

      The index of the row

    • _columnIndex: number

      The index of the column

    Returns number

    The rowspan value (number of rows the cell spans)

    Example

    // Check if a cell spans multiple rows
    const rowspan = bodyAreaModel.getRowspanAt(0, 0);
    if (rowspan > 1) {
    console.log(`This cell spans ${rowspan} rows`);
    }

    Example

    // Implementation example for a model with row spanning
    getRowspanAt(rowIndex: number, columnIndex: number): number {
    // Special case: first column spans 2 rows at the beginning
    if (columnIndex === 0 && rowIndex % 2 === 0) return 2;

    // No spanning for other cells
    return 1;
    }
  • Returns the value of the cell at the specified row and column indices. This is one of the most important methods in the area model, as it provides the actual data to be displayed in each cell.

    Parameters

    • rowIndex: number

      The index of the row

    • columnIndex: number

      The index of the column

    Returns any

    The value at the specified cell

    Example

    // Get the value of a cell
    const value = bodyAreaModel.getValueAt(1, 2);
    console.log(`Cell (1,2) contains: ${value}`);

    Example

    // Implementation example for a 2D array model
    getValueAt(rowIndex: number, columnIndex: number): any {
    if (rowIndex < 0 || rowIndex >= this.data.length) return undefined;
    const row = this.data[rowIndex];
    if (columnIndex < 0 || columnIndex >= row.length) return undefined;
    return row[columnIndex];
    }

    Example

    // Implementation example for an object array model
    getValueAt(rowIndex: number, columnIndex: number): any {
    const row = this.getRowByIndex(rowIndex);
    if (!row) return undefined;

    const property = this.columnDefs[columnIndex].property;
    return row[property];
    }
  • Returns the Y-coordinate (in pixels) of the top of the row at the specified index. This method is used for scrolling and positioning rows in the viewport.

    Parameters

    • rowIndex: number

      The index of the row

    Returns number

    The Y-coordinate of the top of the row, in pixels

    Example

    // Get the Y-position of a row
    const yPos = bodyAreaModel.getYPosByRowIndex(10);
    console.log(`Row 10 starts at Y-position: ${yPos}px`);

    Example

    // Implementation example with fixed row heights
    getYPosByRowIndex(rowIndex: number): number {
    return rowIndex * this.defaultRowHeight;
    }

    Example

    // Implementation example with variable row heights
    getYPosByRowIndex(rowIndex: number): number {
    // Use a pre-calculated array of Y-positions
    return this.yPositions[rowIndex];

    // Or calculate on-the-fly
    // let yPos = 0;
    // for (let i = 0; i < rowIndex; i++) {
    // yPos += this.getRowHeight(i);
    // }
    // return yPos;
    }
  • Initializes the area model. This method is called internally by the TableModel during initialization. It can be used to perform any necessary setup, such as calculating row positions.

    Returns void

    Example

    // Implementation example
    init(): void {
    // Calculate y-positions for all rows
    this.calculateYPositions();

    // Initialize any other internal state
    this.initialized = true;
    }
  • Determines whether this area supports filtering. If true, the area can be filtered using the externalFilterChanged method.

    Returns boolean

    True if the area is filterable, false otherwise

    Example

    // Check if an area is filterable
    const filterable = bodyAreaModel.isFilterable();
    if (filterable) {
    console.log('This area supports filtering');
    }

    Example

    // Implementation example
    isFilterable(): boolean {
    // Only the body area is filterable
    return this.areaIdent === 'body';
    }
  • Determines whether the row at the specified index is checkable. If true, a checkbox will be rendered at the beginning of the row.

    Parameters

    • _rowIndex: number

      The index of the row

    Returns boolean

    True if the row is checkable, false otherwise

    Example

    // Check if a row is checkable
    const checkable = bodyAreaModel.isRowCheckable(1);
    if (checkable) {
    console.log('This row can be checked by the user');
    }

    Example

    // Implementation example
    isRowCheckable(rowIndex: number): boolean {
    // All rows are checkable by default
    return true;

    // Or implement custom logic
    // const row = this.getRowByIndex(rowIndex);
    // return row && !row.locked; // Only unlocked rows are checkable
    }
  • Returns the checked state of the row at the specified index.

    Parameters

    • rowIndex: number

      The index of the row

    Returns undefined | CheckedType

    The checked state of the row (true, false, or 'indeterminate'), or undefined if the row is not checkable

    Example

    // Check if a row is checked
    const checkedState = bodyAreaModel.isRowChecked(1);
    if (checkedState === "full") {
    console.log('This row is checked');
    } else if (checkedState === "semi") {
    console.log('This row is partially checked');
    }

    Example

    // Implementation example using a row selection model
    isRowChecked(rowIndex: number): CheckedType | undefined {
    if (!this.rowSelectionModel) return undefined;

    const row = this.getRowByIndex(rowIndex);
    return this.rowSelectionModel.isRowChecked(row);
    }
  • Determines whether the cell at the specified row and column indices is selectable. If true, the user can select the cell: the table draws the selection only on selectable body cells. All built-in models and AreaModel return true (except the hidden and cover-grid models).

    Parameters

    • _rowIndex: number

      The index of the row

    • _columnIndex: number

      The index of the column

    Returns boolean

    True if the cell is selectable, false otherwise

    Example

    // Check if a cell is selectable
    const selectable = bodyAreaModel.isSelectable(1, 2);
    if (selectable) {
    console.log('This cell can be selected by the user');
    }

    Example

    // Implementation example
    isSelectable(rowIndex: number, columnIndex: number): boolean {
    // All cells are selectable by default
    return true;

    // Or implement custom logic
    // return this.areaIdent === 'body'; // Only body cells are selectable
    }
  • Rebuilds the displayed rows after the expanded state of rows has changed and applies the active filter (see externalFilterChanged) again. The table calls it on every expand/collapse click. Call it yourself after changing row.expanded from code, then repaint with tableApi.repaintHard() (the row count changes).

    Parameters

    • Optional refilter: boolean = true

      run the filter function again (needed after the rows changed). false after a pure expand/collapse: whether a row matches does not depend on it (the table does this on every click – before, the filter ran over all nodes for each click).

    Returns void

    Example

    // expand all first-level rows from code
    const treeModel = tableModel.getBodyModel() as AreaModelTree<OrderNode>;
    for (const row of treeModel.rows) row.expanded = true;
    treeModel.recalcVisibleTreeRows();
    tableApi.repaintHard();
  • Builds the groups again from the current rows – after grouped values were changed outside the table (inside the table, editing a grouped value moves the row by itself). Repaint with tableApi.repaintHard().

    Returns void

    Example

    sales[0].region = "Süd";   // a live update of a grouped value
    body.regroup();
    tableApi.repaintHard();
  • Sets the checked state of the row at the specified index.

    Parameters

    • rowIndex: number

      The index of the row

    • checked: boolean

      The new checked state

    Returns void

    Example

    // Check a row
    bodyAreaModel.setRowChecked(1, true);

    // Uncheck a row
    bodyAreaModel.setRowChecked(2, false);

    Example

    // Implementation example using a row selection model
    setRowChecked(rowIndex: number, checked: boolean): void {
    if (!this.rowSelectionModel) return;

    const row = this.getRowByIndex(rowIndex);
    if (row) {
    this.rowSelectionModel.checkRow(row, checked);
    }
    }
  • Replaces the rows and groups them again (expanded groups, filter and sorting stay). Repaint with tableApi.repaintHard() – the row count changes.

    Parameters

    • rows: T[]

      the new rows

    Returns void

    Example

    body.setRows(await loadSales());
    tableApi.repaintHard();
  • Sorts the rows inside their groups with your function – it gets your objects, as in a flat table. The groups keep their order. Used by tableApi.sort(fn).

    Parameters

    • compareFn: ((a, b) => number)

      compares two of your objects

        • (a, b): number
        • Parameters

          • a: any
          • b: any

          Returns number

    Returns void

    Example

    tableApi.sort((a: Sale, b: Sale) => b.umsatz - a.umsatz);