in js/vnext/grid-view.js [118:380]
initialize({ scrolling, tableClasses, tableAttributes, dataSource, layout }) {
this._tableView = new TableView({
el: this.$el,
layout,
scrolling,
classes: tableClasses,
attributes: tableAttributes,
});
this.layout = layout;
this.model = new Backbone.Model();
this._dataSource = dataSource;
const projections = this._projections = {};
this._registerProjection = proj => {
const name = proj.name;
if (_.has(projections, ['name'])) {
throw new Error('Duplication projectons');
}
/**
* @callback ProjectionHandler
* @param {DataChainState|StructureChainState|ContentChainState} state
* The input state.
* @param {Object} config
* The projection configuration object.
* @return {DataChainState|StructureChainState|ContentChainState|Promise}
* The output state or the `Promise` of the output state.
*/
/**
* @typedef ProjectionDefinition
* @type {Object}
* @property {string} name - name of the projection
* @property {ProjectionHandler} handler
* The callback to transform the state
* @property {Object} defaults
* The default configuration of the projection
* @property {function} normalize
* The callback to normalize the projection configurations.
*/
projections[name] = {
name,
handler: (_.isFunction(proj) ? proj : proj.handler).bind(this),
defaults: proj.defaults,
normalize: (proj.normalize || _.identity).bind(this),
};
return projections[name];
};
/**
* @typedef DataChainState
* @type {Object}
* @property {string} uniqueId
* The unique id for the set of data items. It changes each time new
* query is made to the server.
* @property {string} primaryKey
* The primary key of the data items.
* @property {(Object[]|FakeArray)} items
* An array or a fake array of data items.
* @property {number} totalCount
* The total item count on the server.
* @property {Object.<string, Object>} itemIndex
* The items indexed by primary key.
* @property {external:BackboneViewEventHash} events
* The event has in form of `Backbone.View#events`. Any projection can
* response to the DOM events by adding its own event handlers.
*/
this._chainData = new ProjectionChain(this.model);
/**
* It extends the {@link DataChainState} with extra properties.
* @typedef StructureChainState
* @type {DataChainState}
* @property {ColumnConfig[]} columns
* The array of column configurations.
* @property {RowConfig[]} headRows
* The row configurations for `THEAD`.
* @property {RowConfig[]} bodyRows
* The row configurations for `TBODY`.
* @property {RowConfig[]} footRows
* The row configurations for `TFOOT`.
*/
this._chainStructure = new ProjectionChain(this.model);
/**
* It extends the {@link StructureChainState} with extra properties, and
* overrides some of the properties.
* @typedef ContentChainState
* @type {StructureChainState}
* @property {ColumnGroup} columnGroup
* The column group object, represents the compiled column hierarchy.
* @property {ColContent[]} cols
* The content for `COL` elements in `COLGROUP`.
* @property {RowContent} headRows
* The content for rows in `THEAD`.
* @property {RowContent} bodyRows
* The content for rows in `TBODY`.
* @property {RowContent} footRows
* The content for rows in `TFOOT`.
*/
this._chainContent = new ProjectionChain(this.model);
this.pipeDataProjections(query, buffer, itemIndex);
this.pipeStructureProjections([]
.concat(this.layout === 'flex' ? flexColumns : columns)
.concat([
rows,
selection,
rangeSelection,
])
);
this.pipeContentProjections([
columnGroup,
cells,
sortableHeader,
events,
]);
const patchEvents = state => _.extend(state, {
events: _.mapObject(state.events, handler => handler.bind(this)),
});
const refreshState = {
changes: null,
force: false,
promise: null,
};
this._isRendered = false;
/**
* Refresh the GridView
* @function refresh
* @memberof GridView
* @instance
* @param {boolean} [force=false]
* True for force refresh ignoring the cached states.
*/
const refresh = () => {
const changes = refreshState.changes;
const force = refreshState.force;
refreshState.changes = null;
refreshState.force = false;
/**
* The `GridView` will update its configuration and redraw.
* @event GridView#willUpdate
*/
this.trigger('willUpdate', changes);
// Don't refresh before the view is rendered
if (!this._isRendered) {
this.trigger('didUpdate', changes);
return Promise.resolve();
}
return _.reduce([
this._chainData,
this._chainStructure,
this._chainContent,
], (memo, chain) => chain.update(memo, changes, force), null)
.then(patchEvents)
.then(state => new Promise(resolve => {
this._tableView.set(state, resolve);
}))
.then(nextTick)
.catch(error => {
if (error) {
console.warn(error.stack || error);
}
})
.finally(() => {
/**
* The `GridView` did update its configuration and redraw.
* @event GridView#didUpdate
*/
refreshState.promise = null;
this.trigger('didUpdate', changes);
});
};
const scheduleUpdate = this.refresh = force => {
refreshState.force = force || refreshState.force;
if (refreshState.changes) {
_.extend(refreshState.changes, this.model.changedAttributes());
} else {
refreshState.changes = this.model.changedAttributes();
if (refreshState.promise) {
refreshState.promise = refreshState.promise.then(() => refresh()).finally();
} else {
refreshState.promise = nextTick().then(() => refresh());
}
}
};
this.model.on('change', () => scheduleUpdate(false));
_.each([
/**
* The `GridView` will redraw the DOM. This event may trigger frequently
* when virtualization is enabled.
* @event GridView#willRedraw
*/
'willRedraw',
/**
* The `GridView` did redraw the DOM. This event may trigger frequently
* when virtualization is enabled.
* @event GridView#didRedraw
*/
'didRedraw',
/**
* @event GridView#willRedrawHeader
*/
'willRedrawHeader',
/**
* @event GridView#didRedrawHeader
*/
'didRedrawHeader',
/**
* @event GridView#willRedrawFooter
*/
'willRedrawFooter',
/**
* @event GridView#didRedrawFooter
*/
'didRedrawFooter',
/**
* @event GridView#wwillRedrawBody
*/
'willRedrawBody',
/**
* @event GridView#didRedrawBody
*/
'didRedrawBody',
], event => {
this._tableView.on(event, (...args) => {
this.trigger(event, ...args);
});
});
this.on('all', (event, ...args) => {
const events = _.chain(this._chainContent)
.result('state').result('events').value();
const handler = events && events[event];
if (_.isFunction(handler)) {
handler(...args);
}
});
}