Skip to content

Public Row API Specification

Hristo Anastasov edited this page Jul 14, 2021 · 12 revisions

Public Row API Specification

Contents

  1. Task
  2. Overview
  3. Implementation
  4. Limitations
  5. Changes
  6. Test plan

Revision History

Version User Date Notes
0.1 Hristo Anastasov March 18, 2021 Initial Draft
0.2 Hristo Anastasov March 25, 2021 API polishing, implementation without injecting row
0.3 Hristo Anastasov April 13, 2021 Details, Limitations
0.4 Hristo Anastasov April 13, 2021 Test plan

Currently, grid APIs which return "row objects" are returning the actual row component from current state of the DOM. However this is far from useful as with the current implementation virtualization state is not taken into account (#6158) as well as exposing API which should not be usable by users of the grid.

As a solution, a new row-like interface should be exposed by the grids as the type returned from certain public API calls. This new interface will act as a façade for the core row API (while also taking the current virtualization state into account.

IgxGridRow, IgxTreeGridRow and IgxHierarchicalGridRow are the new classes, which implement the new interface. These classes act as a façade for the corresponding row components: IgxGridRowComponent, IgxTreeGridRowComponent, IgxHierarchicalGridRowComponent. (see below).

getRowByIndex and getRowByKey now should not depend on what is there in the virtualization container. This also includes the pinned rows, groupby rows and summary rows.

Internal functionalities and tests that depend on the rows DOM elements are changed to use gridAPI.get_row_by_index or gridAPI.get_row_by_key to work. Internal functionalities and tests that depend on the rows public API, continue use the public getRowByIndex and getRowByKey.

  1. Declare the public RowType interface, which defines the public properties/methods to be exposed in the public API.
export interface RowType {
    index: number;
    viewIndex?: number;
    isGroupByRow?: boolean;
    isSummaryRow?: boolean;
    summaries?: Map<string, IgxSummaryResult[]>;
    groupRow?: IGroupByRecord;
    /** Deprecated, will be removed. key is the new property */
    rowID?: any;
    key?: any;
    /** Deprecated, will be removed. data is the new property */
    rowData?: any;
    data?: any;
    disabled?: boolean;
    pinned?: boolean;
    selected?: boolean;
    expanded?: boolean;
    deleted?: boolean;
    inEditMode?: boolean;
    children?: RowType[];
    parent?: RowType;
    hasChildren?: boolean;
    treeRow?: ITreeGridRecord;
    grid: GridType;
    update?: (value: any) => void;
    delete?: () => any;
    pin?: () => void;
    unpin?: () => void;
}
  1. Implement the new façade classes, which should implement the interface. This façade class takes the index of the row as parameter in the constructor, along with the corresponding grid component. Those are the only two needed to initialize the facade class instance - the index of the row and the grid.

IgxGridRow, IgxTreeGridRow and IgxHierarchicalGridRow implement the same interface RowType. The interface keeps most properties/methods optional, because igxGroupByRow and IgxSummaryRow also implement the same interface. This makes it possible for developers to always type the value returned from getRowByIndex as RowType regardless if the row at this index would happen to be a summary row or groupby row.

API calls happen over the grid api and grid services and nothing depends on the actual row component anymore.

export class IgxGridRow extends BaseRow{ 
    public get pinned(): boolean {
        return this.grid.isRecordPinned(this.data);
    }

    public set pinned(val: boolean) {
        if (val) {
            this.grid.pinRow(this.key);
        } else {
            this.grid.unpinRow(this.key);
        }
    }
    constructor(protected grid: IgxGridBaseDirective & FlatGridType,
        public index: number, protected _data?: any) {
        super();
    }
}

export class IgxTreeGridRow extends BaseRow {
    public get treeRow(): ITreeGridRecord {
        return this._tgrid.records.get(this.key);
    }

    constructor(protected grid: IgxGridBaseDirective & TreeGridType,
        public index: number, protected _data?: any, private _treeRow?: ITreeGridRecord) {
        super();
    }
}

export class IgxHierarchicalGridRow extends BaseRow {
    constructor(protected grid: IgxGridBaseDirective & HierarchicalGridType,
        public index: number, protected _data?: any) {
        super();
    }

    /**
     * Returns true if row islands exist.
     */
    public get hasChildren(): boolean {
        return  !!this.grid.childLayoutKeys.length;
    }
}
  1. Cell instances continue to take the IgxRowDirective as intRow input for internal use. row exposed by the cell component returns an instance of the corresponding row class.
/** @hidden @internal */
@Input()
public intRow: IgxRowDirective<IgxGridBaseDirective & GridType>;

/**
 * Gets the row of the cell.
 row: RowType = cell.row;
 */
@Input()
public get row(): RowType {
    if (this.grid instanceof IgxTreeGridComponent) {
        return new IgxTreeGridRow(this.grid, this.intRow.index, this.intRow.rowData, (this.intRow as IgxTreeGridRowComponent).treeRow);
    }
    if (this.grid instanceof IgxHierarchicalGridComponent) {
        return new IgxHierarchicalGridRow(this.grid, this.intRow.index, this.intRow.rowData);
    }
    if (this.grid instanceof IgxGridComponent) {
        return new IgxGridRow(this.grid, this.intRow.index, this.intRow.rowData);
    }
}
  1. Make the public row API’s work with instances of the new classes. Those instances are created on demand in corresponding calls:
// grid.component.ts
// tree-grid.component.ts
// hierarchical-grid.component.ts 
public getRowByIndex(index: number): RowType {
    ...
    return this.createRow(index);
}

public getRowByKey(key: any): RowType {
    ...
    return new IgxGridRow(this, index, rec);
}

// cell.component.ts
public get row(): RowType {
    if (this.grid instanceof IgxTreeGridComponent) {
        return new IgxTreeGridRow(this.grid, this.intRow.index, this.intRow.rowData, (this.intRow as IgxTreeGridRowComponent).treeRow);
    } else if (this.grid instanceof IgxGridComponent) {
        return new IgxGridRow(this.grid, this.intRow.index, this.intRow.rowData);
    } else {
        return new IgxHierarchicalGridRow(this.grid, this.intRow.index, this.intRow.rowData);
    }
}
  • getRowByIndex works per page (as it has always been), i.e. first row on any page of the grid has index 0
  • getRowByKey will return undefined for a row that is on a different page, or is not visible if its parent (tree row or groupby row is collapsed)
  • row property emitted in IPinRowEventArgs is now of type RowType
  • dragData property emitted in IRowDragStartEventArgs, IRowDragEndEventArgs is now of type RowType
  • IRowDragStartEventArgs, IRowDragEndEventArgs now emit dragElement property.
  • getRowByIndex will now return an instance of IgxSummaryRow, instead of undefined, if there is a summary row at that index
  • getRowByKey will now return undefined, instead of a row instance, for a row, if its parent groupby row is collapsed. See test "should allows expanding/collapsing groups"
  • rowData is deprecated. data is introduced
  • rowID is deprecated. key is introduced
  • toggle method, exposed by the IgxHierarchicalRowComponent is not available. Use expanded property for all row types

There are currently many tests for all grids that use getRowByIndex, getRowByKey and cell.row public APIs. Many of them verify correct rendering of the DOM elements, the rest of them verify the row public API usage.

  • Those that verify the correct DOM rendering, need to be rewritten to use the gridAPI.get_row_by_index, gridAPI.get_row_by_key and cell.intRow.
  • Those that verify the usage of the public API, continue to use the getRowByIndex, getRowByKey and cell.row. Some of them cast the type returned as IgxRowComponent, those casts need to be removed.

Test cases to be added:

Test Should verify
use getRowByIndex to get a row that is out of the virtualized dom container row is returned
use getRowByIndex to get a row that is first or last row is returned
use getRowByIndex to get a row whose index exceeds last index of the current page undefined is returned
use getRowByIndex(0) on different grid pages always returns the first row on the current page
use getRowByKey to take a record, which is on another grid page returns undefined
groupby in igx-grid, and use use getRowByKey to take a record, whose parent groupby row is collapsed returns undefined
use use getRowByKey to take a record, whose parent tree row is collapsed returns undefined
read row.hasChildren for igx-hierarchical-grid returns true if row has row islands
read row.hasChildren for igx-tree-grid returns true if row has children
read row.hasChildren for igx-grid always returns false
groupBy the igx-grid and enable summaries for column. use getRowByIndex to get a row that happens to be a groupby row or summary row instance of the corresponding IgxGridRow, IgxSummaryRow or IgxGroupBy row to be returned
Add top pinned records in grid. Use getRowByIndex(0) to get the top pinned record. instance of the pinned record to be returned
Add bottom pinned records in grid. Use getRowByIndex(lastIndex) to get the bottom pinned record. instance of the pinned record to be returned
Add pinned records in grid. Use the row API to select/delete the row, etc. Both the data row and the pinned row should reflect the change
Test the row API Updates the corresponding state properly i.e. row.selected = true marks teh row as deleted
Test the row.expanded API Should work properly for IgxGridRow (when the grid has igxGridDetail template), IgxGroupByRow, IgxTreeGridRow and IgxHierarchicalGridRow
Clone this wiki locally