Перейти к основному содержимому

Data Binding & State Management in Angular Scheduler

Angular Scheduler supports two data ownership models:

  1. Angular state as the source of truth (recommended for most applications).
  2. Scheduler as the source of truth (performance-focused for specialized pages).

Choose one model per page/feature area and keep it consistent.

Angular State Or Store As Source Of Truth​

In this model:

  • your component state or RxJS store owns events.
  • the wrapper receives arrays through inputs.
  • chart changes are captured via data.save or data.batchSave.
  • callbacks update your state/store and new arrays flow back into <dhx-scheduler>.

Best for​

  • Angular pages with toolbars/forms that must stay in sync with the chart.
  • team codebases already built around services and RxJS.
  • predictable state transitions and easier debugging.

Tradeoffs​

  • more application-state updates for heavy scheduler operations.
  • more frequent synchronization work during bulk edits.

Anti-patterns to avoid​

  • mutating data through instance while still pushing stale events array from Angular state.
  • ignoring data.save / data.batchSave and expecting scheduler edits to persist in your app state automatically.

Full-flow example (component state)​

import { Component } from "@angular/core";
import {
DhxSchedulerComponent,
type AngularSchedulerDataConfig,
type Event,
} from "@dhtmlx/trial-angular-scheduler";

@Component({
selector: "app-scheduler",
standalone: true,
imports: [DhxSchedulerComponent],
template: `<dhx-scheduler [events]="events" [data]="dataConfig"></dhx-scheduler>`,
})
export class SchedulerComponent {
events: Event[] = [
{
id: 1,
text: "Planning",
start_date: new Date("2026-05-18T09:00:00"),
end_date: new Date("2026-05-18T10:30:00"),
},
];

dataConfig: AngularSchedulerDataConfig = {
save: (entity, action, item, id) => {
if (entity !== "event") return;

if (action === "create") {
this.events = [...this.events, item as Event];
return;
}

if (action === "update") {
this.events = this.events.map(event =>
String(event.id) === String(id) ? { ...(item as Event) } : event
);
return;
}

if (action === "delete") {
this.events = this.events.filter(event => String(event.id) !== String(id));
}
},
};
}

Scheduler As Source Of Truth​

In this model, the chart and backend own most of the runtime data lifecycle.

Best for​

  • very large datasets.
  • scheduler-centric screens.
  • heavy or chained edits where frequent app-store updates are expensive.

Tradeoffs​

  • less immediate visibility of live chart state in Angular services/components.
  • extra discipline required when mixing occasional input updates with imperative operations.

Anti-patterns to avoid​

  • partial mirroring without a clear reconciliation plan.
  • refeeding stale server snapshots after users already changed data in the chart.

Server transport example​

dataConfig = {
save: async (entity: string, action: string, item: any, id: string | number) => {
if (entity !== "event") return;

if (action === "create") {
const response = await fetch("/api/events", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(item),
});

const created = await response.json();
return { id: created.id };
}

if (action === "update") {
await fetch(`/api/events/${id}`, {
method: "PUT",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(item),
});
return;
}

if (action === "delete") {
await fetch(`/api/events/${id}`, { method: "DELETE" });
}
},
};

If your backend replaces temporary IDs on create, return { id: realId } so Scheduler can reconcile client and server IDs.

Callback Contracts​

data.save​

save is passed to scheduler.createDataProcessor(save) and receives per-change payloads.

Typical function shape:

(entity: string, action: string, data: any, id: string | number) => any

For Scheduler event CRUD:

  • entity is "event",
  • action is "create" | "update" | "delete",
  • data is the created, updated, or deleted event,
  • id is the affected event id.

Bulk updates with data.batchSave​

data.batchSave is useful when many changes are emitted in a short time.

Use it when you want to:

  • reduce request count by sending grouped changes.
  • process updates in one reducer or store transaction.
dataConfig = {
batchSave: async changes => {
await fetch("/api/events/batch", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(changes),
});
},
};

Use save for simple per-change logic, and batchSave for grouped synchronization.

Loading Data Into Angular State​

In the Angular-driven model, Scheduler gets data from Angular state. Common sources:

  • local component fields,
  • service state,
  • RxJS store,
  • API calls.

Local state source​

events: Event[] = seedEvents;
date = new Date("2026-05-18T00:00:00");
view = "week";
<dhx-scheduler [events]="events" [date]="date" [view]="view"></dhx-scheduler>

RxJS store source​

Every store follows the same flow:

  • observable reads state,
  • AsyncPipe unwraps a view model,
  • inputs feed Scheduler,
  • data.save or data.batchSave calls store methods.
@if (vm$ | async; as vm) {
<dhx-scheduler
[events]="vm.events"
[date]="vm.date"
[view]="vm.view"
[data]="dataConfig">
</dhx-scheduler>
}

See Using Angular Scheduler with RxJS for a complete example.

API loading source​

async ngOnInit(): Promise<void> {
const response = await fetch("/api/events");
const payload = await response.json();
this.events = payload.events || [];
}

When Angular owns the data, prefer loading into Angular state and passing events to the wrapper.

Scheduler As Source Of Truth​

In this mode, Angular renders the component but does not hold canonical event state.

URL transport example​

<dhx-scheduler
[data]="{
load: '/api/scheduler/load',
save: '/api/scheduler/save'
}">
</dhx-scheduler>

Callback transport example​

dataConfig = {
load: async () => {
const response = await fetch("/api/scheduler/load");
return response.json();
},
save: async (entity, action, item, id) => {
await fetch("/api/scheduler/save", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ entity, action, item, id }),
});
},
};

Use this approach when Scheduler can remain the primary runtime store and Angular does not need to render every individual update.

What's Next​

Need help?
Got a question about the documentation? Reach out to our technical support team for help and guidance. For custom component solutions, visit the Services page.