From 3b006f3c99149a09e0a9544a0edf5b6a9634c4d6 Mon Sep 17 00:00:00 2001 From: Alyar <> Date: Mon, 27 Jul 2026 22:43:03 +0400 Subject: [PATCH] DataGrid: Fix extra request for the first page on load when starting on another page (T1326786) --- .../tests/dataGrid/common/scrolling.ts | 74 +++++++++++++++++++ .../virtual_scrolling/m_virtual_scrolling.ts | 24 ++++-- 2 files changed, 92 insertions(+), 6 deletions(-) diff --git a/e2e/testcafe-devextreme/tests/dataGrid/common/scrolling.ts b/e2e/testcafe-devextreme/tests/dataGrid/common/scrolling.ts index 8cbf5819319f..aa0e74724e04 100644 --- a/e2e/testcafe-devextreme/tests/dataGrid/common/scrolling.ts +++ b/e2e/testcafe-devextreme/tests/dataGrid/common/scrolling.ts @@ -39,6 +39,12 @@ async function getTestLoadCount(): Promise { return ClientFunction(() => (window as any).testLoadCount as number)(); } +async function getTestLoadOptions(): Promise<{ skip: number; take: number }[]> { + return ClientFunction( + () => (window as any).testLoadOptions as { skip: number; take: number }[], + )(); +} + fixture.disablePageReloads`Scrolling` .page(url(__dirname, '../../container.html')); @@ -2161,6 +2167,74 @@ test('DataGrid - The "row" parameter in the FocusedRowChanged event refers to a }); }); +test('Remote virtual scrolling should send one request on init when starting on a non-first page with async templates (T1326786)', async (t) => { + const dataGrid = new DataGrid('#container'); + + await t.expect(dataGrid.isReady()).ok(); + await t.expect(dataGrid.getScrollTop()).gt(0); + await t.expect(dataGrid.apiPageIndex()).eql(10); + + const loadOptions = await getTestLoadOptions(); + + await t.expect(loadOptions).eql([{ skip: 1000, take: 100 }]); +}).before(async () => { + await ClientFunction(() => { + (window as any).testLoadOptions = []; + })(); + + return createWidget('dxDataGrid', () => ({ + height: 1000, + remoteOperations: true, + renderAsync: false, + templatesRenderAsynchronously: true, + dataSource: new (window as any).DevExpress.data.CustomStore({ + key: 'id', + load(loadOptions: any) { + (window as any).testLoadOptions.push({ + skip: loadOptions.skip, + take: loadOptions.take, + }); + + const skip = loadOptions.skip ?? 0; + const take = loadOptions.take ?? 20; + const data: Record[] = []; + + for (let i = skip; i < skip + take; i += 1) { + data.push({ id: i, text: `item ${i}` }); + } + + return Promise.resolve({ data, totalCount: 5000 }); + }, + }), + scrolling: { + mode: 'virtual', + useNative: false, + }, + paging: { + pageSize: 100, + pageIndex: 10, + }, + columns: [ + { dataField: 'id', cellTemplate: 'cell' }, + 'text', + ], + integrationOptions: { + templates: { + cell: { + render(e: any) { + setTimeout(() => { + ($(e.container) as any).text(e.model.value); + e.onRendered?.(); + }); + }, + }, + }, + }, + })); +}).after(async () => ClientFunction(() => { + delete (window as any).testLoadOptions; +})()); + fixture`Scrolling - warnings` .page(url(__dirname, '../../container.html')); diff --git a/packages/devextreme/js/__internal/grids/grid_core/virtual_scrolling/m_virtual_scrolling.ts b/packages/devextreme/js/__internal/grids/grid_core/virtual_scrolling/m_virtual_scrolling.ts index 47536c32d5a9..67596113123a 100644 --- a/packages/devextreme/js/__internal/grids/grid_core/virtual_scrolling/m_virtual_scrolling.ts +++ b/packages/devextreme/js/__internal/grids/grid_core/virtual_scrolling/m_virtual_scrolling.ts @@ -1122,18 +1122,30 @@ export const data = (Base: ModuleType) => class VirtualScrolling } } + private isViewportScrolledToPage(): boolean { + const initialPageScrollPending = isVirtualPaging(this) + && this._rowsScrollController?.getViewportItemIndex() === 0 + && this.pageIndex() > 0; + + return !initialPageScrollPending; + } + private updateViewport() { const viewportSize = this.viewportSize(); const itemCount = this.items().length; const viewportIsNotFilled = viewportSize > itemCount; const currentTake = this._loadViewportParams?.take ?? 0; - const rowsScrollController = this._rowsScrollController; - const newTake = rowsScrollController?.getViewportParams().take; + const newTake = this._rowsScrollController?.getViewportParams().take ?? 0; - (viewportIsNotFilled || currentTake < newTake!) && !this._isPaging && itemCount && this.loadViewport({ - checkLoading: true, - viewportIsNotFilled, - }); + const needsMoreItems = viewportIsNotFilled || currentTake < newTake; + const canLoad = !this._isPaging && this.isViewportScrolledToPage(); // T1326786 + + if (needsMoreItems && canLoad && itemCount) { + this.loadViewport({ + checkLoading: true, + viewportIsNotFilled, + }); + } } private loadIfNeed() {