@eclipse-scout/core
Version:
Eclipse Scout runtime
212 lines (180 loc) • 7.11 kB
text/typescript
/*
* Copyright (c) 2010, 2025 BSI Business Systems Integration AG
*
* This program and the accompanying materials are made
* available under the terms of the Eclipse Public License 2.0
* which is available at https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*/
import {Cell, codes, CodeType, LookupCallColumn, LookupCallOrModel, LookupRow, objects, scout, SmartColumnEventMap, SmartColumnModel, SmartField, TableRow} from '../../index';
/**
* Column where each cell fetches its value using a lookup call.
*
* A 'prepareLookupCall' event gets triggered before executing the lookup call and contains two properties, 'lookupCall' and 'row'. Here, 'lookupCall' is the
* lookup call which is used to fetch one or more values for a cell. 'row' is the row containing the cell and usually corresponds to the selected row.
* It should be used instead of the property selectedRows from Table.js which must not be used here.
* 'row' can be null or undefined in some cases. Hence, some care is needed when listening to this event.
*/
export class SmartColumn<TValue> extends LookupCallColumn<TValue> implements SmartColumnModel<TValue> {
declare model: SmartColumnModel<TValue>;
declare eventMap: SmartColumnEventMap<TValue>;
declare self: SmartColumn<any>;
browseAutoExpandAll: boolean;
browseLoadIncremental: boolean;
activeFilterEnabled: boolean;
protected _lookupCallBatchContext: SmartColumnBatchContext<TValue>;
constructor() {
super();
this.browseAutoExpandAll = true;
this.browseLoadIncremental = false;
this.activeFilterEnabled = false;
this._lookupCallBatchContext = null;
}
protected override _initCell(cell: Cell<TValue>): Cell<TValue> {
super._initCell(cell);
cell.sortCode = this._calculateCellSortCode(cell);
return cell;
}
protected _calculateCellSortCode(cell: Cell<TValue>): number {
if (!this.codeType) {
return cell.sortCode;
}
let codeType = codes.get(this.codeType);
if (!codeType) {
return null;
}
let code = codeType.get(cell.value);
return code ? code.sortCode : null;
}
protected _updateAllCellSortCodes() {
this.table.rows.map(row => this.cell(row)).forEach(cell => cell.setSortCode(this._calculateCellSortCode(cell)));
}
protected override _setLookupCall(lookupCall: LookupCallOrModel<TValue>) {
super._setLookupCall(lookupCall);
if (this.initialized) {
this._updateAllCellSortCodes();
}
}
protected override _setCodeType(codeType: string | (new() => CodeType<TValue>)) {
super._setCodeType(codeType);
if (this.initialized) {
this._updateAllCellSortCodes();
}
}
setBrowseAutoExpandAll(browseAutoExpandAll: boolean) {
this.setProperty('browseAutoExpandAll', browseAutoExpandAll);
}
setBrowseLoadIncremental(browseLoadIncremental: boolean) {
this.setProperty('browseLoadIncremental', browseLoadIncremental);
}
setActiveFilterEnabled(activeFilterEnabled: boolean) {
this.setProperty('activeFilterEnabled', activeFilterEnabled);
}
protected override _formatValue(value: TValue, row?: TableRow): string | JQuery.Promise<string> {
if (!this.lookupCall) {
return scout.nvl(value, '');
}
if (this.lookupCall.batch) {
return this._batchFormatValue(value);
}
let lookupCall = this.lookupCall.clone();
this.trigger('prepareLookupCall', {
lookupCall: lookupCall,
row: row
});
return lookupCall.textByKey(value);
}
/**
* Defers all invocations of the lookup call for the duration of the current event handler.
* Once the current event handler completes, all lookup calls are resolved in a single batch.
*/
protected _batchFormatValue(key: TValue): JQuery.Promise<string> {
if (objects.isNullOrUndefined(key)) {
return $.resolvedPromise('');
}
let currentBatchContext = this._lookupCallBatchContext;
if (!currentBatchContext) {
// create new batch context for this column
const batchResult = $.Deferred();
currentBatchContext = {
keySet: new Set(),
result: batchResult.promise()
};
this._lookupCallBatchContext = currentBatchContext;
setTimeout(() => {
// reset batch context for next batch run
this._lookupCallBatchContext = null;
let lookupCall = this.lookupCall.clone();
this.trigger('prepareLookupCall', {
lookupCall: lookupCall
});
// batch lookup texts
lookupCall.textsByKeys([...currentBatchContext.keySet])
.then(textMap => batchResult.resolve(textMap)) // resolve result in current batch context
.catch(e => batchResult.reject(e)); // reject any errors
});
}
// add key to current batch
currentBatchContext.keySet.add(key);
// return text for current key
return currentBatchContext.result.then(textMap => textMap[objects.ensureValidKey(key)] || '');
}
/**
* Create and set the lookup-row instead of call setValue() as this would execute a lookup by key
* which is not necessary, since the cell already contains text and value. This also avoids a problem
* with multiple lookups running at once, see ticket 236960.
*/
protected override _updateEditorFromValidCell(field: SmartField<TValue>, cell: Cell<TValue>) {
if (objects.isNullOrUndefined(cell.value)) {
field.setValue(null);
return;
}
let lookupRow: LookupRow<TValue> = new LookupRow();
lookupRow.key = cell.value;
lookupRow.text = cell.text;
field.setLookupRow(lookupRow);
}
protected override _createEditor(row: TableRow): SmartField<TValue> {
let field = scout.create(SmartField, {
parent: this.table,
codeType: this.codeType,
lookupCall: this.lookupCall ? this.lookupCall.clone() : null,
browseHierarchy: this.browseHierarchy,
browseMaxRowCount: this.browseMaxRowCount,
browseAutoExpandAll: this.browseAutoExpandAll,
browseLoadIncremental: this.browseLoadIncremental,
activeFilterEnabled: this.activeFilterEnabled
}) as SmartField<TValue>;
field.on('prepareLookupCall', event => {
this.trigger('prepareLookupCall', {
lookupCall: event.lookupCall,
row: row
});
});
field.on('lookupCallDone', event => {
this.trigger('lookupCallDone', {
result: event.result
});
});
return field;
}
/**
* Since we don't know the type of the key from the lookup-row we must deal with numeric and string types here.
*/
protected override _hasCellValue(cell: Cell<TValue>): boolean {
let value = cell.value;
if (objects.isNumber(value)) {
return !objects.isNullOrUndefined(value); // Zero (0) is valid too
}
return !!value;
}
protected override _setCellValue(row: TableRow, value: TValue, cell: Cell<TValue>) {
super._setCellValue(row, value, cell);
cell.setSortCode(this._calculateCellSortCode(cell));
}
}
export type SmartColumnBatchContext<TValue> = {
keySet: Set<TValue>;
result: JQuery.Promise<Record<string, string>>;
};