Remote Data
Sorting, filtering, searching and paging are all delegated to a (simulated) backend with 250 ms latency. Watch the request log: rapid typing produces a single request thanks to debouncing, and a newer request aborts the stale one.
CustomDataSource CursorDataSource LoadOptions AbortSignal
server-side 250ms latency
Request log
- #1 GET /api/employees?skip=0&take=12
import { ChangeDetectionStrategy, Component, inject } from '@angular/core';
import { OgeColumn, OgeGrid } from '@oge-ui/grid';
import { HttpClient } from '@angular/common/http';
import { CustomDataSource } from '@oge-ui/core';
import { firstValueFrom } from 'rxjs';
import type { LoadResult } from '@oge-ui/core';
interface Employee {
id: number;
firstName: string;
department: string;
}
@Component({
selector: 'demo-root',
imports: [OgeColumn, OgeGrid],
changeDetection: ChangeDetectionStrategy.OnPush,
template: `
<oge-grid [data]="source" keyField="id" [filterRow]="true" [searchPanel]="true">
<oge-column field="firstName" />
<oge-column field="department" />
</oge-grid>
`,
})
export class Demo {
private readonly http = inject(HttpClient);
protected readonly source = new CustomDataSource<Employee>({
key: 'id',
load: (options) =>
// options = { skip, take, sort, filter, searchText, … } — serialize as-is
firstValueFrom(
this.http.post<LoadResult<Employee>>('/api/employees/query', options),
),
});
}Cursor-paginated endpoints
Many APIs page with a cursor (?after=… → { items, nextCursor }) and have no offset to jump to. CursorDataSource adapts one to the grid: pair it with infinite scrolling and it walks the cursor chain as you scroll, fetching each page once. The total stays open until the last page arrives; a new sort, filter or search restarts from the first page.
CursorDataSource infinite scrolling
import { ChangeDetectionStrategy, Component, inject } from '@angular/core';
import { OgeColumn, OgeGrid } from '@oge-ui/grid';
import { HttpClient } from '@angular/common/http';
import { CursorDataSource } from '@oge-ui/core';
import { firstValueFrom } from 'rxjs';
import type { CursorPage } from '@oge-ui/core';
interface Account {
id: string;
email: string;
createdAt: string;
}
@Component({
selector: 'demo-root',
imports: [OgeColumn, OgeGrid],
changeDetection: ChangeDetectionStrategy.OnPush,
template: `
<oge-grid
[data]="source"
keyField="id"
[scrolling]="{ mode: 'infinite' }"
style="height: 420px"
>
<oge-column field="email" />
<oge-column field="createdAt" dataType="date" />
</oge-grid>
`,
})
export class Demo {
private readonly http = inject(HttpClient);
// The endpoint pages with a cursor (`?after=…&limit=50` → { items, nextCursor }),
// not with an offset. The source walks the chain as the grid scrolls, fetches
// every page once per query and restarts from the first page when the sort,
// filter or search changes.
protected readonly source = new CursorDataSource<Account, string>({
key: 'id',
pageSize: 50,
fetchPage: ({ cursor, pageSize, sort, filter, searchText }) =>
firstValueFrom(
this.http.post<CursorPage<Account, string>>('/api/accounts/page', {
after: cursor,
limit: pageSize,
sort,
filter,
searchText,
}),
),
});
}Notes
LoadOptionsis a plain serializable object — post it to a .NET / Node endpoint as-is and return{ data, totalCount }.- Every option carries an
AbortSignal: when the state changes mid-flight, the stale request is aborted and can never overwrite newer data. - Filter typing is debounced (
filterDebounce, default 300 ms) so one settled interaction equals one request. - The header filter's distinct values come from the optional
distinct()delegate.