UNPKG

@adaptabletools/adaptable

Version:

Powerful AG Grid extension which provides advanced, cutting-edge functionality to meet all DataGrid requirements

247 lines (246 loc) 11.4 kB
import { CalculatedColumnModuleId } from '../Constants/ModuleConstants'; import { AggregatedScalarLiveValue } from './AggregatedScalarLiveValue'; import * as parser from '../../parser/src'; import Helper from '../Helpers/Helper'; export class CalculatedColumnExpressionService { adaptableApi; aggregatedScalarLiveValuesMap = new Map(); constructor(adaptableApi) { this.adaptableApi = adaptableApi; this.adaptableApi = adaptableApi; } buildSyntheticCellDataChangedInfosForCalcColumns(cellDataChangedInfo) { return buildSyntheticCellDataChangedInfosForCalcColumns(this.adaptableApi, cellDataChangedInfo); } listentoCellDataChange(cellDataChangedInfo) { if (cellDataChangedInfo.trigger === 'calculatedColumnChange') { return; } const refreshedCalculatedColumns = []; this.aggregatedScalarLiveValuesMap.forEach((aggregatedScalarLiveValue, calculatedColumnId) => { const calculatedColumn = this.adaptableApi.calculatedColumnApi.getCalculatedColumnById(calculatedColumnId); const columnDependencies = this.adaptableApi.calculatedColumnApi.internalApi.getReferencedColumnIdsForCalculatedColumn(calculatedColumn); if (columnDependencies.includes(cellDataChangedInfo.column.columnId)) { aggregatedScalarLiveValue.refresh(); refreshedCalculatedColumns.push(calculatedColumn.ColumnId); } }); if (refreshedCalculatedColumns.length) { this.adaptableApi.gridApi.refreshColumns(refreshedCalculatedColumns); } } listentoRowDataChange(rowDataChangedInfo) { const refreshedCalculatedColumns = []; this.aggregatedScalarLiveValuesMap.forEach((aggregatedScalarLiveValue, calculatedColumnId) => { const calculatedColumn = this.adaptableApi.calculatedColumnApi.getCalculatedColumnById(calculatedColumnId); aggregatedScalarLiveValue.refresh(); refreshedCalculatedColumns.push(calculatedColumn.ColumnId); }); if (refreshedCalculatedColumns.length) { this.adaptableApi.gridApi.refreshColumns(refreshedCalculatedColumns); } } destroy() { this.aggregatedScalarLiveValuesMap.clear(); this.aggregatedScalarLiveValuesMap = null; } getCalculatedColumnDataType(calculatedColumnQuery) { try { if (calculatedColumnQuery.AggregatedScalarExpression) { return 'number'; } let firstRowNode = this.adaptableApi.gridApi.getFirstRowNode(); let firstRowValue = this.adaptableApi.internalApi .getQueryLanguageService() .evaluateScalarExpression(calculatedColumnQuery.ScalarExpression, CalculatedColumnModuleId, firstRowNode); if (firstRowValue instanceof Date) { return 'date'; } if (typeof firstRowValue === 'boolean') { return 'boolean'; } if (typeof firstRowValue === 'string') { return 'text'; } if (typeof firstRowValue === 'number') { return 'number'; } if (Array.isArray(firstRowValue)) { return 'numberArray'; } } catch (e) { this.adaptableApi.logError(`Invalid CalculatedColumn expression ${this.adaptableApi.expressionApi.getAdaptableQueryExpression(calculatedColumnQuery)} :: ${e}`); return 'number'; } } isCalculatedColumnQueryValid(calculatedColumnQuery) { if (calculatedColumnQuery.ScalarExpression) { try { let firstRowNode = this.adaptableApi.gridApi.getFirstRowNode(); this.adaptableApi.internalApi .getQueryLanguageService() .evaluateScalarExpression(calculatedColumnQuery.ScalarExpression, CalculatedColumnModuleId, firstRowNode); return true; } catch (e) { return false; } } if (calculatedColumnQuery.AggregatedScalarExpression) { const validationResult = this.adaptableApi.expressionApi.isValidAggregatedScalarExpression(calculatedColumnQuery.AggregatedScalarExpression, CalculatedColumnModuleId); return validationResult; } return false; } evaluateCalculatedColumnQuery(calculatedColumn, rowNode) { try { if (this.adaptableApi.gridApi.isGroupRowNode(rowNode)) { return undefined; } if (calculatedColumn?.Query?.ScalarExpression) { return this.adaptableApi.internalApi .getQueryLanguageService() .evaluateScalarExpression(calculatedColumn.Query.ScalarExpression, CalculatedColumnModuleId, rowNode); } if (calculatedColumn?.Query?.AggregatedScalarExpression) { const aggregatedScalarLiveValue = this.aggregatedScalarLiveValuesMap.get(calculatedColumn.Uuid); return aggregatedScalarLiveValue?.getAggregatedValueForRow(rowNode); } } catch (error) { this.adaptableApi.logError(`Invalid CalculatedColumn expression ${calculatedColumn?.Query?.ScalarExpression ?? calculatedColumn?.Query?.AggregatedScalarExpression} :: ${error}`); return null; } } createAggregatedScalarLiveValue(calculatedColumn) { this.destroyAggregatedScalarLiveValue(calculatedColumn); if (calculatedColumn.Query?.AggregatedScalarExpression) { try { const aggregatedScalarLiveValue = new AggregatedScalarLiveValue({ aggregatedScalarExpression: calculatedColumn.Query?.AggregatedScalarExpression, }, CalculatedColumnModuleId, this.adaptableApi); this.aggregatedScalarLiveValuesMap.set(calculatedColumn.Uuid, aggregatedScalarLiveValue); } catch (e) { this.adaptableApi.logError(`Creating CalculatedColumn ('${calculatedColumn.Query?.AggregatedScalarExpression}') failed!`, e.message); } } } destroyAggregatedScalarLiveValue(calculatedColumn) { this.aggregatedScalarLiveValuesMap.delete(calculatedColumn.Uuid); } } function isScalarCalculatedColumn(calculatedColumn) { return !!calculatedColumn.Query?.ScalarExpression; } function collectDependentScalarCalculatedColumns(adaptableApi, rootColumnId) { const collected = []; const seenCalcColumnIds = new Set(); const queue = [rootColumnId]; while (queue.length > 0) { const columnId = queue.shift(); const adaptableColumn = adaptableApi.columnApi.getColumnWithColumnId(columnId); if (!adaptableColumn) { continue; } const dependentColumnIds = adaptableApi.calculatedColumnApi.internalApi.getCalculatedColumnsDependentOnColumn(adaptableColumn); dependentColumnIds.forEach((dependentColumnId) => { if (seenCalcColumnIds.has(dependentColumnId)) { return; } const calculatedColumn = adaptableApi.calculatedColumnApi.getCalculatedColumnForColumnId(dependentColumnId); if (!calculatedColumn || !isScalarCalculatedColumn(calculatedColumn)) { return; } seenCalcColumnIds.add(dependentColumnId); collected.push(calculatedColumn); queue.push(dependentColumnId); }); } return collected; } function buildShadowRowData(adaptableApi, info) { const shadowData = Helper.cloneObject(info.rowNode?.data ?? info.rowData ?? {}); const fieldName = info.column.field ?? info.column.columnId; adaptableApi.internalApi.setValueUsingField(shadowData, fieldName, info.oldValue); return shadowData; } function evaluateScalarExpressionWithShadowData(adaptableApi, calculatedColumn, rowNode, shadowData) { const internalApi = adaptableApi.internalApi; const moduleFns = internalApi .getQueryLanguageService() .getModuleExpressionFunctionsMap(CalculatedColumnModuleId); const baseFunctions = { ...moduleFns.booleanFunctions, ...moduleFns.scalarFunctions }; const resolveColumnValue = (columnId) => { const childCalculatedColumn = adaptableApi.calculatedColumnApi.getCalculatedColumnForColumnId(columnId); if (childCalculatedColumn?.Query?.ScalarExpression) { return evaluateScalarExpressionWithShadowData(adaptableApi, childCalculatedColumn, rowNode, shadowData); } const adaptableColumn = adaptableApi.columnApi.getColumnWithColumnId(columnId); return internalApi.getValueUsingField(shadowData, adaptableColumn?.field ?? columnId); }; const customFunctions = { ...baseFunctions, COL: { ...baseFunctions.COL, handler: (args) => resolveColumnValue(args[0]), }, FIELD: { ...baseFunctions.FIELD, handler: (args) => internalApi.getValueUsingField(shadowData, args[0]), }, }; return parser.evaluate(calculatedColumn.Query.ScalarExpression, { node: { ...rowNode, data: shadowData }, functions: customFunctions, ...internalApi.buildBaseContext(), }); } export function buildSyntheticCellDataChangedInfosForCalcColumns(adaptableApi, info) { if (!info.rowNode || info.preventEdit || info.trigger === 'undo' || info.trigger === 'aggChange' || info.trigger === 'calculatedColumnChange') { return []; } const calculatedColumns = collectDependentScalarCalculatedColumns(adaptableApi, info.column.columnId); if (!calculatedColumns.length) { return []; } const shadowData = buildShadowRowData(adaptableApi, info); const expressionService = adaptableApi.internalApi.getCalculatedColumnExpressionService(); const syntheticEvents = []; calculatedColumns.forEach((calculatedColumn) => { const oldValue = evaluateScalarExpressionWithShadowData(adaptableApi, calculatedColumn, info.rowNode, shadowData); const newValue = expressionService.evaluateCalculatedColumnQuery(calculatedColumn, info.rowNode); if (oldValue == newValue) { return; } const column = adaptableApi.columnApi.getColumnWithColumnId(calculatedColumn.ColumnId); if (!column) { return; } syntheticEvents.push(adaptableApi.internalApi.buildCellDataChangedInfo({ oldValue, newValue, column, primaryKeyValue: info.primaryKeyValue, rowNode: info.rowNode, trigger: 'calculatedColumnChange', })); }); return syntheticEvents; } export function expandCellDataChangedInfosWithCalculatedColumns(adaptableApi, cellDataChangedInfos) { if (!cellDataChangedInfos.length) { return cellDataChangedInfos; } const syntheticEvents = cellDataChangedInfos.flatMap((info) => buildSyntheticCellDataChangedInfosForCalcColumns(adaptableApi, info)); if (!syntheticEvents.length) { return cellDataChangedInfos; } return [...cellDataChangedInfos, ...syntheticEvents]; }