Remote data
A real calendar holds years of appointments; the scheduler asks for the period on screen. Bind an OgeSchedulerDataSource and its load runs per visible range with instants and an AbortSignal. The previous and next periods are prefetched, quick navigation is debounced, a range the user left is aborted, and every loaded range is cached. A filtering @oge-ui/coreDataSource (OData, a custom source) gets the same per-range loading with remoteFiltering.
OgeSchedulerDataSource load({ startDate, endDate, signal }) prefetch reload() remoteFiltering
Range loading
A fake server with 400 ms latency. Navigate quickly: the log shows the debounce, the prefetched neighbours answering from the cache, and the requests aborted on the way. Drag or edit an appointment — the change goes through the source's update, then the range reloads.
load signal insert / update / remove reload()
Loading appointments…
Sunday, March 1, 2026
Monday, March 2, 2026
Tuesday, March 3, 2026
Wednesday, March 4, 2026
Thursday, March 5, 2026
Friday, March 6, 2026
Saturday, March 7, 2026
- #1 Mar 1 – Mar 7 — loading
import { ChangeDetectionStrategy, Component, signal, viewChild } from '@angular/core';
import { OgeScheduler } from '@oge-ui/scheduler';
import type { OgeSchedulerDataSource, OgeSchedulerLoadOptions } from '@oge-ui/scheduler';
@Component({
selector: 'demo-root',
imports: [OgeScheduler],
changeDetection: ChangeDetectionStrategy.OnPush,
template: `
<!-- load() runs per visible range: the neighbours are prefetched, navigation
is debounced, a range the user navigated away from is aborted through
signal, and every range is cached. Edits go through insert / update /
remove, then the range reloads. -->
<div class="mb-3 flex gap-2">
<button type="button" (click)="scheduler().reload()">Reload</button>
</div>
<oge-scheduler
#cal
[dataSource]="source"
[currentDate]="date"
currentView="week"
[dayStartHour]="8"
[dayEndHour]="18"
style="height: 560px"
/>
`,
})
export class Demo {
protected readonly scheduler = viewChild.required<OgeScheduler<Record<string, unknown>>>('cal');
protected readonly date = new Date(2026, 2, 4);
protected readonly source: OgeSchedulerDataSource<Record<string, unknown>> = {
debounce: 200,
load: async ({ startDate, endDate, signal }: OgeSchedulerLoadOptions) => {
const query = new URLSearchParams({
from: startDate.toISOString(),
to: endDate.toISOString(),
});
// the server returns every appointment overlapping the range, recurring
// series included; the scheduler expands occurrences client-side
const response = await fetch('/api/appointments?' + query.toString(), { signal });
const rows: { id: number; text: string; start: string; end: string }[] =
await response.json();
return rows.map((row) => ({
id: row.id,
text: row.text,
startDate: new Date(row.start),
endDate: new Date(row.end),
}));
},
insert: (item) =>
fetch('/api/appointments', { method: 'POST', body: JSON.stringify(item) }),
update: (key, patch) =>
fetch('/api/appointments/' + String(key), {
method: 'PATCH',
body: JSON.stringify(patch),
}),
remove: (key) =>
fetch('/api/appointments/' + String(key), { method: 'DELETE' }),
};
}