Infinite & Remote Virtual Scroll
One million rows, none of them preloaded. The grid fetches sparse 100-row blocks around the viewport as you scroll — drag the scrollbar to the middle and only that block is requested; everything in between is skipped. Rows still loading render as skeletons.
scrolling remote CustomDataSource
1.000.000 rows 150ms latency
import { ChangeDetectionStrategy, Component } from '@angular/core';
import { OgeColumn, OgeGrid } from '@oge-ui/grid';
import { CustomDataSource } from '@oge-ui/core';
interface Employee {
id: number;
firstName: string;
lastName: string;
department: string;
city: string;
salary: number;
}
const TOTAL = 1_000_000;
@Component({
selector: 'app-employees',
imports: [OgeColumn, OgeGrid],
changeDetection: ChangeDetectionStrategy.OnPush,
template: `
<!-- remote virtual scrolling: sparse 100-row blocks over 1M rows -->
<oge-grid [data]="employees" keyField="id"
[scrolling]="{ mode: 'virtual', remote: true }"
[sortable]="false" class="h-[560px]">
<oge-column field="id" caption="Id" [width]="110" dataType="number" />
<oge-column field="firstName" caption="First Name" />
<oge-column field="lastName" caption="Last Name" />
<oge-column field="department" caption="Department" />
<oge-column field="city" caption="City" />
<oge-column field="salary" caption="Salary" dataType="number" />
</oge-grid>
`,
})
export class EmployeesComponent {
// The grid asks for 100-row blocks as you scroll; nothing else is fetched.
readonly employees = new CustomDataSource<Employee>({
key: 'id',
load: async ({ skip = 0, take = 100 }) => {
const response = await fetch(`/api/employees?skip=${skip}&take=${take}`);
const { data } = await response.json();
return { data, totalCount: TOTAL };
},
});
}Scrolling modes
mode: 'virtual'withremote: true— the scrollbar reflects the fulltotalCount; blocks load on demand with block-level caching and de-duplication.mode: 'infinite'— same block fetching, but the scroll space grows as the user reaches the end; atotalCountis not required.- Sorting or filtering invalidates the block cache and reloads around the current position — the request carries the usual
LoadOptions, so the server stays in charge.