diff --git a/packages/devextreme/js/__internal/grids/grid_core/columns_controller/m_columns_controller.ts b/packages/devextreme/js/__internal/grids/grid_core/columns_controller/m_columns_controller.ts index be977eb272a1..86a1d88a8013 100644 --- a/packages/devextreme/js/__internal/grids/grid_core/columns_controller/m_columns_controller.ts +++ b/packages/devextreme/js/__internal/grids/grid_core/columns_controller/m_columns_controller.ts @@ -20,9 +20,9 @@ import Store from '@js/data/abstract_store'; import filterUtils from '@js/ui/shared/filtering'; import errors from '@js/ui/widget/ui.errors'; import inflector from '@ts/core/utils/m_inflector'; -import type { Column, ColumnsChanges, FilterField } from '@ts/grids/grid_core/columns_controller/types'; -import type { DataController } from '@ts/grids/grid_core/data_controller/data_controller'; -import type { FocusController } from '@ts/grids/grid_core/focus/m_focus'; +import type { + Column, ColumnsChanges, FilterChangeDetector, FilterField, +} from '@ts/grids/grid_core/columns_controller/types'; import type { StateStoringController } from '@ts/grids/grid_core/state_storing/state_storing_controller_core'; import { AI_COLUMN_NAME } from '../ai_column/const'; @@ -133,9 +133,7 @@ export class ColumnsController extends modules.Controller { public _columnChanges?: ColumnsChanges; - protected _dataController!: DataController; - - protected _focusController!: FocusController; + private filterChangeDetector?: FilterChangeDetector; protected _stateStoringController!: StateStoringController; @@ -162,9 +160,11 @@ export class ColumnsController extends modules.Controller { }; } + public setFilterChangeDetector(detector: FilterChangeDetector): void { + this.filterChangeDetector = detector; + } + public init(isApplyingUserState?: boolean): void { - this._dataController = this.getController('data'); - this._focusController = this.getController('focus'); this._stateStoringController = this.getController('stateStoring'); const columns = this.option('columns'); @@ -1336,8 +1336,7 @@ export class ColumnsController extends modules.Controller { updateColumnChanges(this, 'grouping'); } - if (this._dataController - && !gridCoreUtils.equalFilterParameters(parameters.filtering, this._dataController.getCombinedFilter(), langParams)) { + if (this.filterChangeDetector?.(parameters.filtering, langParams)) { updateColumnChanges(this, 'filtering'); } updateColumnChanges(this, 'columns'); diff --git a/packages/devextreme/js/__internal/grids/grid_core/columns_controller/types.ts b/packages/devextreme/js/__internal/grids/grid_core/columns_controller/types.ts index dd25e14b8867..d0fd99adda92 100644 --- a/packages/devextreme/js/__internal/grids/grid_core/columns_controller/types.ts +++ b/packages/devextreme/js/__internal/grids/grid_core/columns_controller/types.ts @@ -15,6 +15,10 @@ export type FilterField = Omit & { filterOperations? export type AddedColumn = string | (Column & { columns?: (Column | string)[] }); +// Both operands stay `unknown`: the point of injecting the detector is that +// `ColumnsController` answers a boolean question without knowing what a filter is. +export type FilterChangeDetector = (lastLoadFilter: unknown, langParams: unknown) => boolean; + export interface InternalColumnOptions { parseValue?: (text: string) => unknown; deserializeValue?: (value: unknown) => unknown; diff --git a/packages/devextreme/js/__internal/grids/grid_core/data_controller/data_controller.ts b/packages/devextreme/js/__internal/grids/grid_core/data_controller/data_controller.ts index f04907c7bc8e..02030c2df3fa 100644 --- a/packages/devextreme/js/__internal/grids/grid_core/data_controller/data_controller.ts +++ b/packages/devextreme/js/__internal/grids/grid_core/data_controller/data_controller.ts @@ -159,6 +159,13 @@ export class DataController extends modules.Controller { this.dataPushedHandlerProxy = this.dataPushedHandler.bind(this); this._columnsController.columnsChanged.add(this.columnsChangedHandler.bind(this)); + this._columnsController.setFilterChangeDetector( + (lastLoadFilter, langParams) => !gridCoreUtils.equalFilterParameters( + lastLoadFilter, + this.getCombinedFilter(), + langParams, + ), + ); this._isLoading = false; this._isCustomLoading = false; diff --git a/packages/devextreme/js/__internal/grids/grid_core/focus/m_focus.ts b/packages/devextreme/js/__internal/grids/grid_core/focus/m_focus.ts index 39fb37dd160b..cbed7b7477c0 100644 --- a/packages/devextreme/js/__internal/grids/grid_core/focus/m_focus.ts +++ b/packages/devextreme/js/__internal/grids/grid_core/focus/m_focus.ts @@ -530,16 +530,26 @@ const focusEditorFactoryViewControllerExtender = ( }; const columns = (Base: ModuleType) => class FocusColumnsExtender extends Base { + protected focusController!: FocusController; + + protected dataController!: DataController; + + public init(isApplyingUserState?: boolean): void { + this.focusController = this.getController('focus'); + this.dataController = this.getController('data'); + + super.init(isApplyingUserState); + } + public getSortDataSourceParameters(_, sortByKey?) { // @ts-expect-error let result = super.getSortDataSourceParameters.apply(this, arguments); - const dataSource = this._dataController._dataSource; - const store = this._dataController.store(); - let key = store && store.key(); + const dataSource = this.dataController._dataSource; + let key = dataSource?.store()?.key(); const remoteOperations = dataSource && dataSource.remoteOperations() || {}; const isLocalOperations = Object.keys(remoteOperations).every((operationName) => !remoteOperations[operationName]); - if (key && (this.option('focusedRowEnabled') && this._focusController.isAutoNavigateToFocusedRow() !== false || sortByKey)) { + if (key && (this.option('focusedRowEnabled') && this.focusController.isAutoNavigateToFocusedRow() !== false || sortByKey)) { key = Array.isArray(key) ? key : [key]; const notSortedKeys = key.filter((key) => !this.columnOption(key, 'sortOrder')); diff --git a/packages/devextreme/testing/helpers/gridBaseMocks.js b/packages/devextreme/testing/helpers/gridBaseMocks.js index ee32c7d22640..69cf530b79d1 100644 --- a/packages/devextreme/testing/helpers/gridBaseMocks.js +++ b/packages/devextreme/testing/helpers/gridBaseMocks.js @@ -369,6 +369,10 @@ module.exports = function($, gridCore, columnResizingReordering, domUtils, commo columnsChanged: $.Callbacks(), + // Accepted so the real DataController can register from init(); this mock + // never runs _updateChanges, so the detector has nothing to drive. + setFilterChangeDetector: commonUtils.noop, + getColumns: function() { return columns; }, @@ -1171,6 +1175,15 @@ module.exports = function($, gridCore, columnResizingReordering, domUtils, commo options && options.controllers && $.extend(that._controllers, options.controllers); options && options.views && $.extend(that._views, options.views); + // The real DataController registers this from its init(); a mock has no init(), + // so register on its behalf to keep the 'filtering' change type reachable. + const mockDataController = that._controllers.data; + if(that._controllers.columns && mockDataController && !mockDataController.init) { + that._controllers.columns.setFilterChangeDetector(function(lastLoadFilter, langParams) { + return !gridCore.equalFilterParameters(lastLoadFilter, mockDataController.getCombinedFilter(), langParams); + }); + } + $.each(that._controllers, function(name) { that[name + 'Controller'] = this; this.init && this.init();