diff --git a/packages/devextreme/js/__internal/ui/collection/m_collection_widget.live_update.ts b/packages/devextreme/js/__internal/ui/collection/m_collection_widget.live_update.ts index f34d4db5fd94..db85a0ae025d 100644 --- a/packages/devextreme/js/__internal/ui/collection/m_collection_widget.live_update.ts +++ b/packages/devextreme/js/__internal/ui/collection/m_collection_widget.live_update.ts @@ -19,21 +19,15 @@ export default CollectionWidget.inherit({ }); }, - ctor() { - this.callBase.apply(this, arguments); - - this._customizeStoreLoadOptions = (e) => { - const dataController = this._dataController; - - if (dataController.getDataSource() && !this._dataController.isLoaded()) { - this._correctionIndex = 0; - } - if (this._correctionIndex && e.storeLoadOptions) { - e.storeLoadOptions.skip += this._correctionIndex; - } - }; + _customizeStoreLoadOptions(e) { + const dataController = this._dataController; - this._dataController?.on('customizeStoreLoadOptions', this._customizeStoreLoadOptions); + if (dataController.getDataSource() && !this._dataController.isLoaded()) { + this._correctionIndex = 0; + } + if (this._correctionIndex && e.storeLoadOptions) { + e.storeLoadOptions.skip += this._correctionIndex; + } }, reload() { @@ -44,6 +38,7 @@ export default CollectionWidget.inherit({ this.callBase(); this._refreshItemsCache(); this._correctionIndex = 0; + this._subscribeLoadOptionsCustomization(true); }, _findItemElementByKey(key) { @@ -146,7 +141,7 @@ export default CollectionWidget.inherit({ }, _dispose() { - this._dataController.off('customizeStoreLoadOptions', this._customizeStoreLoadOptions); + this._subscribeLoadOptionsCustomization(false); this.callBase(); }, @@ -242,6 +237,19 @@ export default CollectionWidget.inherit({ domAdapter.insertElement($container.get(0), $itemFrame.get(0), nextSiblingElement); }, + _subscribeLoadOptionsCustomization(enable: boolean): void { + if (!this._dataController) { + return; + } + + if (enable) { + this._correctionIndex = 0; + this._dataController.on('customizeStoreLoadOptions', this._customizeStoreLoadOptions.bind(this)); + } else { + this._dataController.off('customizeStoreLoadOptions', this._customizeStoreLoadOptions.bind(this)); + } + }, + _optionChanged(args) { switch (args.name) { case 'items': { @@ -256,7 +264,9 @@ export default CollectionWidget.inherit({ this.option('items', []); } + this._subscribeLoadOptionsCustomization(false); this.callBase(args); + this._subscribeLoadOptionsCustomization(true); break; case 'repaintChangesOnly': break; diff --git a/packages/devextreme/testing/tests/DevExpress.ui/collectionWidgetParts/liveUpdateTests.js b/packages/devextreme/testing/tests/DevExpress.ui/collectionWidgetParts/liveUpdateTests.js index b18ee15d8ef0..be3594f0de0d 100644 --- a/packages/devextreme/testing/tests/DevExpress.ui/collectionWidgetParts/liveUpdateTests.js +++ b/packages/devextreme/testing/tests/DevExpress.ui/collectionWidgetParts/liveUpdateTests.js @@ -183,4 +183,58 @@ module('live update', { assert.strictEqual($items.first().text(), 'text 0 mark', 'the first item correctly updated'); assert.strictEqual($items.last().text(), 'text 24 mark', 'the last item correctly updated'); }); + + test('next page items should not be excessive skipped after dataSource runtime change and push remove (T1250900)', function(assert) { + const newDataSource = new DataSource({ + load: () => helper.data.sort((a, b) => a.index - b.index), + loadMode: 'raw', + pageSize: 2, + pushAggregationTimeout: 0, + key: 'id' + }); + helper.instance.option('dataSource', newDataSource); + + let items = helper.getItems(); + assert.strictEqual(items.length, 2, '2 items on the first page'); + assert.strictEqual(items[0].id, 0, '0 item'); + assert.strictEqual(items[1].id, 1, '1st item'); + + newDataSource.store().push([{ type: 'remove', key: 0 }]); + + items = helper.getItems(); + assert.strictEqual(items.length, 1, '1 item on the first page after remove'); + assert.strictEqual(items[0].id, 1, '1 item'); + + helper.instance.loadNextPage(); + + items = helper.getItems(); + assert.strictEqual(items.length, 3, '2 pages are loaded'); + assert.strictEqual(items[0].id, 1, '1 item'); + assert.strictEqual(items[1].id, 2, '2 item'); + assert.strictEqual(items[2].id, 3, '3 item'); + }); + + test('dataSource runtime change should be correct even if remove was pushed to the previous dataSource', function(assert) { + const data = [...helper.data]; + helper.store.push([{ type: 'remove', key: 0 }]); + + const newDataSource = new DataSource({ + load: (e) => data.sort((a, b) => a.index - b.index), + loadMode: 'raw', + pageSize: 2, + pushAggregationTimeout: 0, + key: 'id' + }); + helper.instance.option('dataSource', newDataSource); + + let items = helper.getItems(); + assert.strictEqual(items[0].id, 0, '0 item'); + assert.strictEqual(items[1].id, 1, '1 item'); + + helper.instance.loadNextPage(); + + items = helper.getItems(); + assert.strictEqual(items[2].id, 2, '2 item'); + assert.strictEqual(items[3].id, 3, '3 item'); + }); });