Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Additional improvements to Store and Record API #1082

Merged
merged 14 commits into from
Apr 22, 2019
Merged
14 changes: 12 additions & 2 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,15 +18,25 @@
`agOptions.defaultGroupSortComparator`).
* The `Column.cellClass` and `Column.headerClass` configs now accept functions to dynamically
generate custom classes based on the Record and/or Column being rendered.
* The `Record` object now provides an additional getter `Record.allChildren` to return all children of the
record, irrespective of the current filter in place on the record's store. This supplements the
existing `Record.children` getter, which returns only the children meeting the filter.

### 💥 Breaking Changes
* The class `LocalStore` has been renamed `Store`, and is now the main implementation and base class
for Store Data. The extraneous abstract superclass `BaseStore` has been removed.
* `LocalStore.dataLastUpdated` had been renamed `Store.lastUpdated` on the new class and is now a
simple timestamp (ms) rather than a javascript date object.
* The argument `LocalStore.processRawData` now expects a function that *returns* a modified object with the
necessary edits. This allows implementations to safely *clone* the raw data rather than mutating it.

### ⚙️ Technical

* `Grid` now performs an important performance workaround when loading a new dataset that would
result in the removal of a significant amount of existing records/rows. The underlying ag-Grid
component has a serious bottleneck here (acknowledged as AG-2879 in their bug tracker). The Hoist
grid wrapper will now detect when this is likely and proactively clear all data using a different
API call before loading the new dataset.
* The implementations of Hoist store classes, `RecordSet`, and `Record` have been updated to more
* The implementations `Store`, `RecordSet`, and `Record` have been updated to more
efficiently re-use existing record references when loading, updating, or filtering data in a
store. This keeps the Record objects within a store as stable as possible, and allows additional
optimizations by ag-Grid and its `deltaRowDataMode`.
Expand Down
8 changes: 3 additions & 5 deletions cmp/grid/Grid.js
Original file line number Diff line number Diff line change
Expand Up @@ -338,7 +338,7 @@ export class Grid extends Component {
{agGridModel, store} = model;

return {
track: () => [agGridModel.agApi, store.records, store.dataLastUpdated],
track: () => [agGridModel.agApi, store.records, store.lastUpdated],
run: ([api, records]) => {
if (!api) return;

Expand All @@ -363,10 +363,8 @@ export class Grid extends Component {
console.debug(`Loaded ${records.length} records into ag-Grid: ${Date.now() - now}ms`);

// Set flag if data is hierarchical.
this._isHierarchical = model.store.allRecords.some(
rec => rec.parentId != null
);

this._isHierarchical = store.allRootCount != store.allCount;

// Increment version counter to trigger selectionReaction w/latest data.
this._dataVersion++;
});
Expand Down
16 changes: 9 additions & 7 deletions cmp/grid/GridModel.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
import {HoistModel, LoadSupport, XH} from '@xh/hoist/core';
import {Column, ColumnGroup} from '@xh/hoist/cmp/grid';
import {AgGridModel} from '@xh/hoist/cmp/ag-grid';
import {BaseStore, LocalStore, StoreSelectionModel} from '@xh/hoist/data';
import {Store, StoreSelectionModel} from '@xh/hoist/data';
import {
ColChooserModel as DesktopColChooserModel,
StoreContextMenu
Expand Down Expand Up @@ -58,7 +58,7 @@ export class GridModel {
//------------------------
// Immutable public properties
//------------------------
/** @member {BaseStore} */
/** @member {Store} */
store;
/** @member {StoreSelectionModel} */
selModel;
Expand Down Expand Up @@ -109,8 +109,8 @@ export class GridModel {
/**
* @param {Object} c - GridModel configuration.
* @param {Object[]} c.columns - {@link Column} or {@link ColumnGroup} configs
* @param {(BaseStore|Object)} [c.store] - a Store instance, or a config with which to create a
* default LocalStore. If not supplied, store fields will be inferred from columns config.
* @param {(Store|Object)} [c.store] - a Store instance, or a config with which to create a
* Store. If not supplied, store fields will be inferred from columns config.
* @param {boolean} [c.treeMode] - true if grid is a tree grid (default false).
* @param {(StoreSelectionModel|Object|String)} [c.selModel] - StoreSelectionModel, or a
* config or string `mode` with which to create one.
Expand Down Expand Up @@ -575,7 +575,7 @@ export class GridModel {
parseStore(store) {
store = withDefault(store, {});

if (store instanceof BaseStore) {
if (store instanceof Store) {
return store;
}

Expand All @@ -587,18 +587,20 @@ export class GridModel {
colFieldNames = uniq(compact(map(this.getLeafColumns(), 'field'))),
missingFieldNames = difference(colFieldNames, storeFieldNames);

pull(missingFieldNames, 'id');

if (missingFieldNames.length) {
store = {
...store,
fields: [...fields, ...missingFieldNames]
};
}

return this.markManaged(new LocalStore(store));
return this.markManaged(new Store(store));
}

throw XH.exception(
'The GridModel.store config must be either a concrete instance of BaseStore or a config to create one.');
'The GridModel.store config must be either a concrete instance of Store or a config to create one.');
}

parseSelModel(selModel) {
Expand Down
111 changes: 0 additions & 111 deletions data/BaseStore.js

This file was deleted.

8 changes: 0 additions & 8 deletions data/Field.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,6 @@
* Copyright © 2019 Extremely Heavy Industries Inc.
*/

import {Record} from '@xh/hoist/data/Record';
import {throwIf} from '@xh/hoist/utils/js';
import {startCase, isEqual as lodashIsEqual} from 'lodash';
import {XH} from '@xh/hoist/core';

Expand Down Expand Up @@ -38,12 +36,6 @@ export class Field {
label = startCase(name),
defaultValue = null
}) {

throwIf(
Record.RESERVED_FIELD_NAMES.includes(name),
`Field name "${name}" cannot be used. It is reserved as a top-level property of the Record class.`
);

this.name = name;
this.type = type;
this.label = label;
Expand Down
152 changes: 0 additions & 152 deletions data/LocalStore.js

This file was deleted.

Loading