Skip to content

Row and cell properties as metadata

Jonathan Eiten edited this page Feb 27, 2018 · 2 revisions

Introduction

Metadata is defined as information about other data. In Hypergrid, metadata refer to properties that help display the actual grid data.

Row and cell properties collections are row-specific data that parallel the row data (as "row metadata" or simply "metadata"). Both these collections can include all the usual Hypergrid properties, such as color, alignment, cell dimensions, padding, what formatter and/or cell renderer to use, etc.

Metadata is stored either:

  • with the data itself, persisted to the back end
  • in a temporary parallel array, lost when the application is closed

Storing metadata with the data rows makes it especially convenient to persist the metadata along with the actual row data. Persisted metadata has advantages, such as for frequently changing rows and cells and/or when the application does not necessarily know what rows and cells need decorating.

Where the metadata is stored is handled by the data model when it implements getRowMetaData and setRowMetaData. Otherwise, the fallbacks for these methods store the metadata temporarily in a parallel array.

The previous implementation of metadata

Prior to Hypergrid 3.0.0, metadata was implemented as a hard-coded hack, using getRow().__META for the hash of column properties objects (keyed by column name) and getRow().__ROW for row properties. This was a hack because among other things it assumed that the object returned by getRow() was easily extensible. This worked fine for the local data model, but we can no longer assume this. The legacy implementation was also inflexible. It always stored metadata along with the actual data; and always stored it using __META and __ROW.

By relying instead on the data model to provide this logic, the data model now has the option to support persisted metadata however it likes by implementing the two new methods are described in this document. The details of the implementation are hidden from Hypergrid. If the data source does not implement the new interface, Hypergrid falls back to transient (non-persisted) storage of row and cell properties.

New metadata methods

v3.0.0 introduces two new data model methods, setRowMetadata and getRowMetadata, which set and get a single metadata object. This change does several things:

  1. Defines a single metadata object. (Previously we had two separate metadata objects for row and cell properties.)
  2. Formalizes metadata access without the use of getRow().
  3. Hides the implementation details from Hypergrid by encapsulating metadata access inside the data source.

It is now up to the data source how to store the metadata. (Although the new local data source, datasaur-local, actually uses the same dataRow.__META property as before for what is essentially the same hash of column properties objects.)

Metadata examples

The following examples show how we are no longer dependent on getRow() or __META.

These examples are for illustrative purposes only. In practice, developers access cell properties with setCellProperty and getCellProperty (and other sister) functions in Behavior.js and Column.js; and row properties with getRowProperties, setRowProperty, setRowProperties, and addRowProperties. (To access the height row property specifically, developers can use setRowHeight and getRowHeight.)

setRowMetadata(rowIndex, newMetadataObject)

Sets metadata object destructively, overwriting any existing metadata object:

dataSource.setRowMetadata(99, { null: 30, lastName: { color: 'red' } });

Omitting the second parameter deletes the metadata:

dataSource.setRowMetadata(99); // deletes row 99's metadata

getRowMetadata(rowIndex, prototype)

The following example:

  1. Gets existing metadata object; else creates and returns it.
  2. Sets font color for a particular cell.
  3. Sets a row property:
var props = dataSource.getRowMetadata(99) || dataSource.setRowMetadata(99, {});
(props.lastName = props.lastName || {}).color = 'red';
(props.__ROW = props.__ROW || {}).backgroundColor = 'yellow';

The first line in the preceding example can be simplified by providing a second parameter to getRowMetadata which creates and embeds a new object, based on the given prototype, guaranteeing an object is returned without an additional conditional call to setRowMetadata:

var props = dataSource.getRowMetadata(99, null);

This is more performant because it saves doing the row lookup twice.

Note that null creates a metadata object without a prototype, which is fine. To have it create the more familiar object based on Object, give Object.prototype instead of null.

Fallbacks for metadata methods

If these methods are unimplemented in the data source, Hypergrid's fallbacks for these methods warehouse the metadata in a parallel array (which would have to be saved and reloaded separately to persist the row and cell properties).

Data structure change for row properties object

To conform to the "single metadata object" concept, the row properties object is now just another metadata property (though still named __ROW). This means that while cell properties is a backwards-compatible data structure, row properties is not. The row properties object has been moved to inside the metadata object (dataRow.__META.__ROW). The previous definition of row properties (dataRow.__ROW) is now undefined.

This is a breaking change. Not terrible as the chances are there are few if any apps out there actually dependent on this data structure for row height. Although I realize we could have avoided the change by coding getRowMetadata to return it inside a single metadata object even if it wasn't actually stored that way, this would have meant building a metadata object on every call and because it's called so frequently (in the low-level loop for cell rendering), this definitely would have impacted performance. Therefore I believe it is worth the change.