Interface: DataModel

DataModel

Hypergrid 3 data models have a minimal required interface, as outlined on the Data Model API wiki page.

TL;DR

The minimum interface is an object with just three methods: getRowCount() getSchema() and getValue(x, y).

Methods

addListener(handler)

IMPLEMENTATION OF THIS METHOD IS OPTIONAL. If your data model does not implement this method, Local#resetDataModel adds the default implementation from polyfills.js. If your data model does implement it, it should also implement the sister methods dispatchEvent, removeListener, and removeAllListeners, because they all work together and you don't want to mix native implementations with polyfills.

Hypergrid calls this method subscribe to data model events. The data model calls its own implementation of dispatchEvent to publish events to subscribers.

Both the addListener polyfill as well as datasaur-base's implementation service multiple listeners for the use case of multiple grid instances all using the same data model instance. To support this use case, your data model should service multiple listeners as well. (Doing so also lets the application add its own listener(s) to the data model.)

Parameters:
Name Type Description
handler DataModelListener

A reference to a function bound to a grid instance. The function is called whenever the data model calls its DataModel#dispatchEvent method. The handler thus receives all data model events (in `event.type).

addRow(yopt, dataRow)

IMPLEMENTATION OF THIS METHOD IS OPTIONAL.

Insert or append a new row.

Parameters:

Parameters:
Name Type Attributes Default Description
y number <optional>
Infinity

The index of the new row. If y >= row count, row is appended to end; otherwise row is inserted at y and row indexes of all remaining rows are incremented.

dataRow object

apply()

IMPLEMENTATION OF THIS METHOD IS OPTIONAL.

Transforms the data. All the rows are subject to change, including the row count.

DataModelError() → {Array.<dataRowObject>}

A direct descendent of Error.

Returns:
Type
Array.<dataRowObject>

delRow(y, rowCountopt) → {Array.<dataRowObject>}

IMPLEMENTATION OF THIS METHOD IS OPTIONAL.

Rows are removed entirely and no longer render. Indexes of all remaining rows are decreased by rowCount.

Parameters:

Parameters:
Name Type Attributes Default Description
y number
rowCount number <optional>
1
Returns:
Type
Array.<dataRowObject>

dispatchEvent(event)

IMPLEMENTATION OF THIS METHOD IS OPTIONAL. If your data model does not implement this method, Local#resetDataModel adds the default implementation from polyfills.js. If your data model does implement it, it should also implement the sister methods addListener, removeListener, and removeAllListeners, because they all work together and you don't want to mix native implementations with polyfills.

If addListener is not implemented, Hypergrid falls back to a simpler approach, injecting its own implementation of dispatchEvent, bound to the grid instance, into the data model. If the data model already has such an implementation, the assumption is that it was injected by another grid instance using the same data model. The newly injected implementation will call the original injected implementation, thus creating a chain. This is an inferior approach because grids cannot easily unsubscribe themselves. Applications can remove all subscribers in the chain by deleting the implementation of dispatchEvent (the end of the chain) from the data model.

Parameters:

Parameters:
Name Type Description
event DataModelEvent

drillDownCharMap() → {Array.<dataRowObject>}

Characters that can be used to construct cell values representing drill downs in a tree structure.

A specialized cell renderer is often employed to produce better results using graphics instead of characters.

Returns:
Type
Array.<dataRowObject>

fetchData(rectangles) → {function}

IMPLEMENTATION OF THIS METHOD IS OPTIONAL.

Tells dataModel what cells will be needed by subsequent calls to getValue(). This helps remote or virtualized data models fetch and cache data. If your data model doesn't need to know this, don't implement it.

Parameters:

Parameters:
Name Type Description
rectangles Array.<Rectangle>

Unordered list of rectangular regions of cells to fetch in a single (atomic) operation.

Returns:

[callback] - Optional callback. If provided, implementation calls it with false on success (requested data fully fetched) or true on failure.

Type
function

getCell(config, rendererName) → {CellRenderer}

Renderer configuration interceptor.

IMPLEMENTATION OF THIS METHOD IS OPTIONAL. If your data model does not implement this method, Local#resetDataModel adds the default implementation (module:hooks.getCell).

Description

This method is a hook called on every cell just prior to rendering and is intended to be overridden.

The first parameter to this method, config, Please refer to the renderConfig object for details. Most of the properties on this object can be overridden programmatically in this method. Properties typically overridden include renderer, editor, format, and the various permutations of font, color, and halign.

Your override will be called with the data model as it's execution context (the this value).

The only requirement for this method (or its override) is to return a reference to an instantiated cell renderer, which is all the default implementation does. This is typically the renderer whose name is in the config.renderer, property (assumed to be a "registered" cell renderer). It doesn't have to be that cell renderer, however; and any object with a paint method will do.

IMPORTANT CAVEAT!!

Although this hook was designed to be overridden, adding lots (or any, really) programmatic logic to be executed on every cell, on every render, is expensive in terms of performance, and doing so should only be a last resort.

As Hypergrid has evolved, many properties have been added including row and cell properties, which can accomplish much that was previously impossible without overrideing getCell. For example, you can now select a formatter and a renderer simply by setting a column's (or row's) (or cell's) format or renderer property.

Overriding getCell still has great facility when the rendering needs to be a function of the data values, but other than that, every effort should be made to avoid overriding getCell whenever possible.

Parameters:

Parameters:
Name Type Description
config CellEditor#renderConfig
rendererName string

Same as config.renderer, the proposed cell renderer name.

Returns:

An instantiated cell renderer.

Type
CellRenderer

getCellEditorAt(columnIndex, rowIndex, editorName, cellEvent) → {undefined|CellEditor}

Instantiate a new cell editor.

IMPLEMENTATION OF THIS METHOD IS OPTIONAL. If your data model does not implement this method, Local#resetDataModel adds the default implementation (module:hooks.getCellEditorAt).

Description

The application developer may override this method to:

  • Instantiate and return an arbitrary cell editor. The generic implementation here simply returns the declared cell editor. This is undefined when there was no such declaration, or if the named cell editor was not registered.
  • Return undefined for no cell editor at all. The cell will not be editable.
  • Set properties on the instance by passing them in the options object. These are applied to the new cell editor object after instantiation but before rendering.
  • Manipulate the cell editor object (including its DOM elements) after rendering but before DOM insertion.

Overriding this method with a null function (that always returns undefined) will have the effect of making all cells uneditable.

The only requirement for this method (or its override) is to return a reference to an instantiated cell editor, which is all the default implementation does. This is typically the cell editor whose name is in the config.editor property (assumed to be a "registered" cell editor). It doesn't have to be that cell editor, however; any object conforming to the CellEditor interface will do.

Parameters:

Parameters:
Name Type Description
columnIndex number

Absolute column index. I.e., the position of the column in the data source's original fields array, as echoed in behavior.allColumns[].

rowIndex number

Row index of the data row in the current list of rows, regardless of vertical scroll position, offset by the number of header rows (all the rows above the first data row including the filter row). I.e., after subtracting out the number of header rows, this is the position of the data row in the index array of the data source (i.e., the last data source pipeline).

editorName string

The proposed cell editor name (from the render properties).

cellEvent CellEvent

All enumerable properties of this object will be copied to the new cell editor object for two purposes:

  • Used in cell editor logic.
  • For access from the cell editor's HTML template (via mustache).

Developer's override of this method may add custom properties, for the purposes listed above.

Hypergrid adds the following properties, required by CellEditor:

  • .format - The cell's format render prop (name of localizer to use to format the editor preload and parse the edited value). May be undefined (no formatting or parsing). Added by calling getCellEditorAt method. Developer's override is free to alter this property. .column (Column object), the only enumerable property of the native CellEvent object. Read-only.

Note: The editPoint property formerly available to cell editors in version 1 has been deprecated in favor of cellEvent.gridCell.

Returns:

An object instantiated from the registered cell editor constructor named in editorName. A falsy return means the cell is not editable because the editorName was not registered.

Type
undefined | CellEditor

getColumnCount() → {number}

Same as getSchema().length.

Returns:

Number of columns in the schema.

Type
number

getData(metadataFieldNameopt) → {Array.<dataRowObject>}

IMPLEMENTATION OF THIS METHOD IS OPTIONAL.

All grid data.

Parameters:

Parameters:
Name Type Attributes Description
metadataFieldName string <optional>

If provided, the output will include the row metadata object in a "hidden" field with this name.

Returns:

All the grid's data rows.

Type
Array.<dataRowObject>

getDataIndex(y) → {number}

IMPLEMENTATION OF THIS METHOD IS OPTIONAL.

Synonym for getRowIndex.

Parameters:

Parameters:
Name Type Description
y number

Transformed data row index.

Returns:

Untransformed data row index.

Type
number

getMetadataStore()

IMPLEMENTATION OF THIS METHOD IS OPTIONAL.

Get the metadata store. The precise type of this object is implementation-dependent so not defined here.

datasaur-base supplies fallback implementations of this method as well as DataModel#setMetadataStore which merely get and set this.metadata in support of DataModel#getRowMetadata and DataModel#setRowMetadata.

Custom data models are not required to implement them if they don't need them.

Hypergrid never calls getMetadataStore itself. If implemented, Hypergrid does make a single call to setMetadataStore when data model is reset (see Local#resetDataModel) with no arguments.

Returns:

Metadata store object.

getRow(rowIndex) → {number|undefined}

IMPLEMENTATION OF THIS METHOD IS OPTIONAL.

Get a row of data.

The injected default implementation is an object of lazy getters.

Parameters:

Parameters:
Name Type Description
rowIndex number
Returns:

The data row corresponding to the given rowIndex; or undefined if no such row.

Type
number | undefined

getRowCount() → {number}

Returns:

The number of data rows currently contained in the model.

Type
number

getRowIndex(y) → {number}

IMPLEMENTATION OF THIS METHOD IS OPTIONAL.

Only called by Hypergrid when it receives the data-prereindex or data-postreindex events. These events are typically triggered before and after data model remaps the rows (in its apply method).

Parameters:

Parameters:
Name Type Description
y number

Transformed data row index.

Returns:

Untransformed data row index.

Type
number

getRowMetadata(y, prototypeopt) → {undefined|false|object}

IMPLEMENTATION OF THIS METHOD IS OPTIONAL.

Get the row's metadata object, which is a hash of cell properties objects, for those cells that have property overrides, keyed by column name; plus a row properties object with key __ROW when there are row properties.

The default implementations of getRowMetadata and setRowMetadata store the metadata in an in-memory table. If this is not appropriate, override these methods to store the meta somewhere else (e.g., with the data in a hidden column, in another database table, in local storage, etc.).

Parameters:

Parameters:
Name Type Attributes Description
y number

Row index.

prototype object <optional>

When row found but no metadata found, set the row's metadata to new object created from this object when defined. Typical defined value is null, which creates a plain object with no prototype, or Object.prototype for a more "natural" object.

Returns:

One of:

  • object - existing metadata object or new metadata object created from prototype; else
  • false - row found but no existing metadata and prototype was not defined; else
  • undefined - no such row
Type
undefined | false | object

getSchema() → {Array.<columnSchema>}

Get list of columns. The order of the columns in the list defines the column indexes.

On initial call and again whenever the schema changes, the data model must dispatch the fin-hypergrid-schema-loaded event, which tells Hypergrid to decorate the schema and recreate the column objects.

Returns:
Type
Array.<columnSchema>

getValue(columnIndex, rowIndex) → {string|number|boolean|null}

Get a cell's value given its column & row indexes.

Parameters:

Parameters:
Name Type Description
columnIndex number
rowIndex number
Returns:

The member with the given columnIndex from the data row with the given rowIndex.

Type
string | number | boolean | null

install(api, options)

Install methods into data model.

IMPLEMENTATION OF THIS METHOD IS OPTIONAL. If your data model does not implement this method, Local#resetDataModel adds the default implementation from polyfills.js and then uses it to install the other polyfills and fallbacks when no native implementations exist, thus ensuring there are implementations for Hypergrid to call.

Catcher functions

Catcher functions catch calls made by Hypergrid to otherwise unimplemented functions so such calls fail silently rather than throw an error.

  • When api is array: install injects catcher functions named for each string in the array.
  • When api is an object: install injects catcher functions named for each key in the object. (Keys with non-function values are excluded.)

Keys constructor and initialize are always excluded. In addition, the install method defined in datasaur-base will also exclude keys missing from whitelist (api['!!keys']) or included in blacklist (api['!key']).

The type of catcher function depends on the data model:

  • Flat data model: Catcher functions are simple no-ops that fail silently.
  • Stacked data model: Catcher functions forward calls down the stack to the first native implementation found. When no native implementation is found, the call then fails silently.

Fallbacks methods

Instead of failing silently, Hypergrid can instead install fallback methods for missing implementations:

When options.inject is truthy (and api is an object), install injects api's fallback methods into the data model instead of simple catchers when no native implementation already exists (however see options.force below).

For a stacked data model, the target for the injection is the bottom instance in the stack (the data source instance); and the forwarding mechanism is also installed so the call will find the fallback there.

Forced installation

When options.force is truthy, the fallback methods are always installed regardless of whether or not a native implementation exists; i.e., the fallbacks override the native implementations.

Parameters:
Name Type Attributes Description
api object | Array.<string>

Collection of methods or a list of method names.

The following keys are however ignored:

  • When api is a hash:
    • Keys defined as accessors (getters/setters)
    • Keys not defined as functions
  • Keys named initialize or constructor
  • Keys not named in api['!!keys'] if defined (i.e., a whitelist)
  • Keys named in api['!keys'] if defined (i.e., a blacklist)
options object
options.inject boolean <optional>

Injects api object's fallback methods for missing data model implementations. (Otherwise, just installs catchers based on the keys.)

options.force boolean <optional>

If options.inject also true, injects api object's fallback methods, even if data model already has an implementation.

isTree() → {boolean}

IMPLEMENTATION OF THIS METHOD IS OPTIONAL. It is only required for data models that support tree views.

Returns:

The grid view is a tree (presumably has a tree column).

Type
boolean

isTreeCol(columnIndex) → {boolean}

IMPLEMENTATION OF THIS METHOD IS OPTIONAL. It is only required for data models that support tree views.

Parameters:

Parameters:
Name Type Description
columnIndex number
Returns:

This column is the tree column (displays tree structure; may or may not be an interactive drill-down control).

Type
boolean

removeAllListeners()

IMPLEMENTATION OF THIS METHOD IS OPTIONAL. If your data model does not implement this method, Local#resetDataModel adds the default implementation from polyfills.js. If your data model does implement it, it should also implement the sister methods addListener, dispatchEvent, and removeListener, because they all work together and you don't want to mix native implementations with polyfills.

Removes all data model event handlers, detaching the data model from all grid instances.

This method is not called by Hypergrid but might be useful to applications for resetting a data model instance.

removeListener(handler)

IMPLEMENTATION OF THIS METHOD IS OPTIONAL. If your data model does not implement this method, Local#resetDataModel adds the default implementation from polyfills.js. If your data model does implement it, it should also implement the sister methods addListener, dispatchEvent, and removeAllListeners, because they all work together and you don't want to mix native implementations with polyfills.

Detaches the data model from a particular grid instance.

This method is called by Hypergrid#desctruct to clean up memory. Note: destruct is not called automatically by Hypergrid; applications must call it explicitly when disposing of a grid.

Parameters:
Name Type Description
handler DataModelListener

A reference to the handler originally provided to DataModel#addListener.

setData(data)

IMPLEMENTATION OF THIS METHOD IS OPTIONAL.

Blah.

Parameters:

Parameters:
Name Type Description
data Array.<dataRowObject>

An array of congruent raw data objects.

Array.<rawColumnSchema>

Ordered array of column schema.

setMetadataStore(newMetadataStoreopt)

IMPLEMENTATION OF THIS METHOD IS OPTIONAL.

Set the metadata store. The precise type of this object is implementation-dependent, so not defined here.

datasaur-base supplies fallback implementations of this method as well as DataModel#getMetadataStore which merely set and get this.metadata in support of DataModel#setRowMetadata and DataModel#getRowMetadata.

Custom data models are not required to implement them if they don't need them.

If implemented, Hypergrid makes a single call to setMetadataStore when data model is reset (see Local#resetDataModel) with no arguments. Therefore this method needs to expect a no-arg overload and handle it appropriately.

Hypergrid never calls getMetadataStore.

Parameters:

Parameters:
Name Type Attributes Description
newMetadataStore <optional>

New metadata store object. Omitted on data model reset.

setRow(y, dataRowopt)

IMPLEMENTATION OF THIS METHOD IS OPTIONAL.

Update or blank a row in place, without deleting the row (and without affecting succeeding rows' indexes).

Parameters:

Parameters:
Name Type Attributes Description
y number
dataRow object <optional>

if omitted or otherwise falsy, row renders as blank

setRowMetadata(y, newMetadataopt)

IMPLEMENTATION OF THIS METHOD IS OPTIONAL.

Set the row's metadata object, which is a hash of cell properties objects, for those cells that have property overrides, keyed by column name; plus a row properties object with key __ROW when there are row properties.

The default implementations of getRowMetadata and setRowMetadata store the metadata in an in-memory table. If this is not appropriate, override these methods to store the meta somewhere else (e.g., with the data in a hidden column, in another database table, in local storage, etc.).

Parameters:

Parameters:
Name Type Attributes Description
y number

Row index.

newMetadata object <optional>

When omitted, delete the row's metadata.

setSchema(newSchemaopt)

IMPLEMENTATION OF THIS METHOD IS OPTIONAL.

Define column indexes. May include header, type, and calculator properties for each column.

When the schema changes, the data model should dispatch the fin-hypergrid-schema-loaded event, which tells Hypergrid to decorate the schema and recreate the column objects.

It is not necessary to call on every data update when you expect to reuse the existing schema.

Parameters:

Parameters:
Name Type Attributes Description
newSchema Array.<(columnSchema|string)> <optional>

String elements are immediately converted (by decorate) to columnSchema objects.

setValue(columnIndex, rowIndex, newValue)

IMPLEMENTATION OF THIS METHOD IS OPTIONAL.

Set a cell's value given its column & row indexes and a new value.

Parameters:

Parameters:
Name Type Description
columnIndex number
rowIndex number
newValue *

toggleRow(rowIndex, columnIndexopt, toggleopt) → {boolean|undefined}

Mouse was clicked on a grid row.

IMPLEMENTATION OF THIS METHOD IS OPTIONAL.

Hypergrid calls this method from one place, behavior.cellClicked, which is called from src/features/CellClick when user clicks on a tree or data cell.

The data model may consume or ignore the click.

If the data model consumes the click by modifying some data in the existing data set, it should dispatch the 'fin-hypergrid-data-loaded` data event to the grid, which causes a grid "repaint" (which re-renders rows and columns in place).

If the data model consumes the click by transforming the data, it should dispatch the following data events to the grid:

  • 'fin-hypergrid-data-prereindex' before transforming the data
  • 'fin-hypergrid-data-postreindex' after transforming the data

This causes Hypergrid to save the current row and/or column selections before and then attempt to restore them after, before a "shape change" (which recalculates row and column bounding rects and then re-renders them).

"Transforming the data" means altering the data set (by adding/removing rows, etc.). The typical use case for this is a click on a cell containing a drill-down control.

After rerendering, Hypergrid dispatches a DOM event with the same type (same event string) to the grid's <canvas> element for the benefit of any application listeners.

Parameters:

Parameters:
Name Type Attributes Description
rowIndex number
columnIndex number <optional>

For the drill-down control use case, implementations should call this.isTreeCol(columnIndex) if they want to restrict the response to clicks in the tree column (rather than any column). Although defined in Hypergrid's call, implementations should not depend on it, which may be useful for testing purposes.

toggle boolean <optional>

One of:

  • undefined (or omitted) - Toggle row.
  • true - Expand row iff currently collapsed.
  • false - Collapse row iff currently expanded.

    NOTE: Implementation of this parameter is optional. It may be useful for testing purposes but Hypergrid does not define actual parameter in its call in Hypergrid#cellClicked.

Returns:

If click was consumed by the data model:

  • undefined Not consumed: Row had no drill-down control.
  • true Consumed: Row had a drill-down control which was toggled.
  • false Not consumed: Row had a drill-down control but it was already in requested state.

    NOTE: Implementation of a return value is optional as of version v3.0.0. It may be useful for testing purposes but Hypergrid#cellClicked no longer uses the return value (depending instead on the implementation dispatching data events), so implementations no longer need to support it. Therefore, in general, applications should no depend on a return value. For particular requirements, however, an applications may make a private contract with a data model implementation for a return value (that may or may not follow the above definition. Regardless of the implementation, the return value of this method is propagated through the return values of Local#cellClicked -> Hypergrid#cellClicked -> the application.

Type
boolean | undefined

Events

fin-hypergrid-data-loaded

The data model should trigger this event when it changes the data on its own. Hypergrid responds by calling grid.repaint() — before triggering a grid event using the same event string, which applications can listen for using addEventListener:

grid.addEventListener('fin-hypergrid-data-loaded', myHandlerFunction);

This event is not cancelable.

fin-hypergrid-data-postreindex

The data models should trigger this event immediately after data model remaps the rows. Hypergrid responds by reselecting the remaining rows matching the indices previously saved in the data-prereindex event, and then calling grid.behaviorShapeChanged() — before triggering a grid event using the same event string, which applications can listen for using addEventListener:

grid.addEventListener('fin-hypergrid-data-postreindex', myHandlerFunction);

This event is not cancelable.

fin-hypergrid-data-prereindex

The data models should trigger this event immediately before data model remaps the rows. Hypergrid responds by saving the underlying row indices of currently selected rows — before triggering a grid event using the same event string, which applications can listen for using addEventListener:

grid.addEventListener('fin-hypergrid-data-prereindex', myHandlerFunction);

This event is not cancelable.

fin-hypergrid-data-shape-changed

The data model should trigger this event when it changes the data rows (count, order, etc.) on its own. Hypergrid responds by calling grid.behaviorChanged() — before triggering a grid event using the same event string, which applications can listen for using addEventListener: `js grid.addEventListener('fin-hypergrid-data-shape-changed', myHandlerFunction); This event is not cancelable.

fin-hypergrid-schema-loaded

The data models should trigger this event on a schema change, typically from setSchema, or wherever schema is initialized. Hypergrid responds by normalizing and decorating the schema object and recreating the grid's column objects — before triggering a grid event using the same event string, which applications can listen for using addEventListener:

grid.addEventListener('fin-hypergrid-schema-loaded', myHandlerFunction);

This event is not cancelable.

See: