import {
_,
Autowired,
CellNavigationService,
Component,
Constants,
Context,
Events,
EventService,
GridApi,
GridOptions,
GridOptionsWrapper,
GridRow,
IRowModel,
PinnedRowModel,
PreConstruct,
PostConstruct,
RefSelector,
RowNode,
ValueService,
IStatusPanelComp
} from 'ag-grid-iux-community';
import {RangeController} from "../../rangeController";
import {NameValueComp} from "./nameValueComp";
export class AggregationComp extends Component implements IStatusPanelComp {
private static TEMPLATE = `
`;
@Autowired('eventService') private eventService: EventService;
@Autowired('rangeController') private rangeController: RangeController;
@Autowired('valueService') private valueService: ValueService;
@Autowired('cellNavigationService') private cellNavigationService: CellNavigationService;
@Autowired('pinnedRowModel') private pinnedRowModel: PinnedRowModel;
@Autowired('rowModel') private rowModel: IRowModel;
@Autowired('context') private context: Context;
@Autowired('gridOptionsWrapper') private gridOptionsWrapper: GridOptionsWrapper;
@Autowired('gridOptions') private gridOptions: GridOptions;
@Autowired('gridApi') private gridApi: GridApi;
@RefSelector('sumAggregationComp') private sumAggregationComp: NameValueComp;
@RefSelector('countAggregationComp') private countAggregationComp: NameValueComp;
@RefSelector('minAggregationComp') private minAggregationComp: NameValueComp;
@RefSelector('maxAggregationComp') private maxAggregationComp: NameValueComp;
@RefSelector('avgAggregationComp') private avgAggregationComp: NameValueComp;
constructor() {
super(AggregationComp.TEMPLATE);
}
@PreConstruct
private preConstruct(): void {
this.instantiate(this.context);
}
@PostConstruct
private postConstruct(): void {
if (!this.isValidRowModel()) {
console.warn(`ag-Grid: agSelectedRowCountComponent should only be used with the client and server side row model.`);
return;
}
this.eventService.addEventListener(Events.EVENT_RANGE_SELECTION_CHANGED, this.onRangeSelectionChanged.bind(this));
this.eventService.addEventListener(Events.EVENT_MODEL_UPDATED, this.onRangeSelectionChanged.bind(this));
}
private isValidRowModel() {
// this component is only really useful with client or server side rowmodels
const rowModelType = this.gridApi.getModel().getType();
return rowModelType === 'clientSide' || rowModelType !== 'serverSide';
}
public init() {
}
private setAggregationComponentValue(aggFuncName: string, value: number, visible: boolean) {
let statusBarValueComponent = this.getAggregationValueComponent(aggFuncName);
if (_.exists(statusBarValueComponent)) {
statusBarValueComponent.setValue(_.formatNumberTwoDecimalPlacesAndCommas(value));
statusBarValueComponent.setVisible(visible);
}
}
private getAggregationValueComponent(aggFuncName: string): NameValueComp {
// converts user supplied agg name to our reference - eg: sum => sumAggregationComp
let refComponentName = `${aggFuncName}AggregationComp`;
// if the user has specified the agAggregationPanelComp but no aggFuncs we show the all
// if the user has specified the agAggregationPanelComp and aggFuncs, then we only show the aggFuncs listed
let statusBarValueComponent: NameValueComp = null;
const aggregationPanelConfig = _.exists(this.gridOptions.statusBar) ? _.find(this.gridOptions.statusBar.statusPanels, aggFuncName) : null;
if (_.exists(aggregationPanelConfig)) {
// a little defensive here - if no statusPanelParams show it, if componentParams we also expect aggFuncs
if (!_.exists(aggregationPanelConfig.statusPanelParams) ||
(_.exists(aggregationPanelConfig.statusPanelParams) &&
_.exists(aggregationPanelConfig.statusPanelParams.aggFuncs) &&
_.exists(_.find(aggregationPanelConfig.statusPanelParams.aggFuncs, (item) => item === aggFuncName)))
) {
statusBarValueComponent = (this)[refComponentName];
}
} else {
// components not specified - assume we can show this component
statusBarValueComponent = (this)[refComponentName];
}
// either we can't find it (which would indicate a typo or similar user side), or the user has deliberately
// not listed the component in aggFuncs
return statusBarValueComponent;
}
private onRangeSelectionChanged(): void {
let cellRanges = this.rangeController.getCellRanges();
let sum = 0;
let count = 0;
let numberCount = 0;
let min: number = null;
let max: number = null;
let cellsSoFar: any = {};
if (!_.missingOrEmpty(cellRanges)) {
cellRanges.forEach((cellRange) => {
// get starting and ending row, remember rowEnd could be before rowStart
let startRow = cellRange.start.getGridRow();
let endRow = cellRange.end.getGridRow();
let startRowIsFirst = startRow.before(endRow);
let currentRow = startRowIsFirst ? startRow : endRow;
let lastRow = startRowIsFirst ? endRow : startRow;
while (true) {
let finishedAllRows = _.missing(currentRow) || lastRow.before(currentRow);
if (finishedAllRows) {
break;
}
cellRange.columns.forEach((column) => {
// we only want to include each cell once, in case a cell is in multiple ranges
let cellId = currentRow.getGridCell(column).createId();
if (cellsSoFar[cellId]) {
return;
}
cellsSoFar[cellId] = true;
let rowNode = this.getRowNode(currentRow);
if (_.missing(rowNode)) {
return;
}
let value = this.valueService.getValue(column, rowNode);
// if empty cell, skip it, doesn't impact count or anything
if (_.missing(value) || value === '') {
return;
}
// see if value is wrapped, can happen when doing count() or avg() functions
if (value.value) {
value = value.value;
}
if (typeof value === 'string') {
value = Number(value);
}
if (typeof value === 'number' && !isNaN(value)) {
sum += value;
if (max === null || value > max) {
max = value;
}
if (min === null || value < min) {
min = value;
}
numberCount++;
}
count++;
});
currentRow = this.cellNavigationService.getRowBelow(currentRow);
}
});
}
let gotResult = count > 1;
let gotNumberResult = numberCount > 1;
// we show count even if no numbers
this.setAggregationComponentValue('count', count, gotResult);
// show if numbers found
this.setAggregationComponentValue('sum', sum, gotNumberResult);
this.setAggregationComponentValue('min', min, gotNumberResult);
this.setAggregationComponentValue('max', max, gotNumberResult);
this.setAggregationComponentValue('avg', (sum / numberCount), gotNumberResult);
}
private getRowNode(gridRow: GridRow): RowNode {
switch (gridRow.floating) {
case Constants.PINNED_TOP:
return this.pinnedRowModel.getPinnedTopRowData()[gridRow.rowIndex];
case Constants.PINNED_BOTTOM:
return this.pinnedRowModel.getPinnedBottomRowData()[gridRow.rowIndex];
default:
return this.rowModel.getRow(gridRow.rowIndex);
}
}
}