Die beste Methode, um dhtmlxScheduler mit einem Backend zu verbinden, ist die Einrichtung einer RESTful API auf dem Server und die Verwendung von dhtmlxDataProcessor auf der Client-Seite.
DataProcessor ist eine clientseitige Bibliothek, die in dhtmlxScheduler.js enthalten ist. Sie verfolgt Datenänderungen und verwaltet Serveranfragen vom Client.
Sie können dhtmlxScheduler mithilfe einer REST API mit dem Server verbinden – und das in verschiedenen Frameworks und Programmiersprachen. Nachfolgend finden Sie eine Liste serverseitiger Implementierungen, die für die Scheduler-Backend-Integration verfügbar sind:
Um Daten vom Server über eine REST API zu laden, gehen Sie im Allgemeinen wie folgt vor:
1) Verwenden Sie die Methode load und geben Sie die URL an, die Scheduler-Daten im JSON-Format zurückgibt.
2) Verwenden Sie die Methode createDataProcessor und übergeben Sie ein Objekt mit Konfigurationsoptionen:
const dp = scheduler.createDataProcessor({
url: "apiUrl",
mode: "REST"
});
Alternativ können Sie einen dataProcessor mit dem Konstruktor erstellen und an das dhtmlxScheduler-Objekt anhängen. Der scheduler.DataProcessor()-Konstruktor akzeptiert den Pfad zum serverseitigen Skript:
scheduler.init("scheduler_here", new Date(), "month");
scheduler.load("apiUrl");
const dp = new scheduler.DataProcessor("apiUrl");
dp.init(scheduler);
Ausführlichere Informationen finden Sie im folgenden Abschnitt.
Beim Erstellen eines DataProcessor über die API-Methode createDataProcessor haben Sie mehrere Möglichkeiten, Parameter zu übergeben.
1. Verwenden Sie einen der vordefinierten Request-Modi, zum Beispiel:
const dp = scheduler.createDataProcessor({
url: "/api",
mode: "REST"
});
wobei gilt:
2. Übergeben Sie ein benutzerdefiniertes router-Objekt:
const dp = scheduler.createDataProcessor(router);
wobei der Router eine Funktion sein kann:
const server = "/api";
// entity - "event"
// action - "create"|"update"|"delete"
// data - ein Objekt mit Eventdaten
// id – die ID des bearbeiteten Objekts (Event)
const dp = scheduler.createDataProcessor(function(entity, action, data, id) {
switch(action) {
case "create":
return scheduler.ajax.post(
`${server}/${entity}`,
data
);
break;
case "update":
return scheduler.ajax.put(
`${server}/${entity}/${id}`,
data
);
break;
case "delete":
return scheduler.ajax.del(
`${server}/${entity}/${id}`
);
break;
}
});
oder ein Objekt, das wie folgt aufgebaut ist:
const dp = scheduler.createDataProcessor({
event: {
create: function(data) {},
update: function(data, id) {},
delete: function(id) {}
}
});
Alle Router-Funktionen sollten entweder ein Promise oder ein Datenantwort-Objekt zurückgeben. Dadurch kann der dataProcessor die Datenbank-ID zuweisen und das onAfterUpdate-Event auslösen.
const router = function(entity, action, data, id) {
return new Promise(function(resolve, reject) {
// … Logik
return resolve({tid: databaseId});
});
}
So kann DataProcessor verwendet werden, um Daten in localStorage oder einem beliebigen Speicher, der nicht an eine bestimmte URL gebunden ist, zu speichern oder wenn unterschiedliche Server (URLs) für das Erstellen und Löschen von Objekten zuständig sind.
Die URL folgt diesem Muster:
wobei "api" die in der dataProcessor-Konfiguration angegebene URL ist.
Um den REST-Modus zu verwenden, setzen Sie die Eigenschaft mode
des createDataProcessor-Konfigurationsobjekts auf "REST":
const dp = scheduler.createDataProcessor({
url: "apiUrl",
mode: "REST"
});
Hier sind die typischen Anfragen und Antworten:
Aktion | HTTP-Methode | URL | Antwort |
---|---|---|---|
Daten laden | GET | /apiUrl | JSON-Format |
Neues Event hinzufügen | POST | /apiUrl | {"action":"inserted","tid":"eventId"} |
Event aktualisieren | PUT | /apiUrl/:id | {"action":"updated"} |
Event löschen | DELETE | /apiUrl/:id | {"action":"deleted"} |
Um den REST-JSON-Modus zu verwenden, setzen Sie die Eigenschaft mode
des createDataProcessor-Konfigurationsobjekts auf "REST-JSON":
const dp = scheduler.createDataProcessor({
url: "apiUrl",
mode: "REST-JSON"
});
In diesem Modus sendet der Scheduler POST/PUT/DELETE-Anfragen mit dem Content-Type application/json
.
Die Anfragen und Antworten sehen folgendermaßen aus:
Aktion | HTTP-Methode | URL | Request Body | Antwort |
---|---|---|---|---|
Daten laden | GET | /apiUrl | JSON-Format | |
Neues Event hinzufügen | POST | /apiUrl | { "start_date":"2019-12-18 00:00", "end_date":"2019-12-18 00:05", "text":"New event", ... } |
{ "action":"inserted", "tid":"eventId" } |
Event aktualisieren | PUT | /apiUrl/:id | { "start_date":"2024-12-18 00:00", "end_date":"2024-12-18 00:05", "text":"New event", ... } |
{"action":"updated"} |
Event löschen | DELETE | /apiUrl/:id | {"action":"deleted"} |
Um den POST-Modus zu verwenden, setzen Sie die Eigenschaft mode
des createDataProcessor-Konfigurationsobjekts auf "POST":
const dp = scheduler.createDataProcessor({
url: "apiUrl",
mode: "POST"
});
So funktionieren die Anfragen und Antworten:
Aktion | HTTP-Methode | URL | Antwort |
---|---|---|---|
Daten laden | GET | /apiUrl | JSON-Format |
Event aktualisieren | POST | /apiUrl | {"action":"inserted|updated|deleted", "tid":"eventId"} |
Um den JSON-Modus zu verwenden, setzen Sie die Eigenschaft mode
des createDataProcessor-Konfigurationsobjekts auf "JSON":
const dp = scheduler.createDataProcessor({
url: "apiUrl",
mode: "JSON"
});
In diesem Modus sendet der Scheduler nach jeder Datenänderung eine POST-Anfrage an den Server (ähnlich wie im POST-Modus, aber mit anderem Anfrageformat).
Die Anfragen und Antworten sehen wie folgt aus:
Aktion | HTTP-Methode | Request Body | Antwort |
---|---|---|---|
Daten laden | GET | JSON-Format | |
Neues Event hinzufügen | POST | {
"id": temporaryId,
"action":"inserted", "data":{ "start_date":"2019-12-18 00:00", "end_date":"2019-12-18 00:05", "text":"New event", ... } } |
{ "action":"inserted", "tid":"eventId" } |
Event aktualisieren | POST | {
"id": id,
"action":"updated",
"data":{ "start_date":"2019-12-18 00:00", "end_date":"2019-12-18 00:05", "text":"New event", ... } } |
{"action":"updated"} |
Event löschen | POST | {
"id": id,
"action":"deleted",
"data":{ "start_date":"2019-12-18 00:00", "end_date":"2019-12-18 00:05", "text":"New event", ... } } |
{"action":"deleted"} |
Die Anfragen und Antworten für das dynamische Laden sind wie folgt:
Aktion | HTTP-Methode | URL | Antwort |
---|---|---|---|
Daten laden | GET | /apiUrl?from=minDate&to=maxDate | JSON-Format |
Create/Update/Delete-Anfragen beinhalten alle öffentlichen Eigenschaften des clientseitigen Event-Objekts:
Der Parameter !nativeeditor_status gilt nur für den POST-Modus.
Immer wenn im Scheduler eine Aktion ausgeführt wird, wie das Hinzufügen, Aktualisieren oder Löschen von Ereignissen, reagiert dataProcessor, indem eine AJAX-Anfrage an den Server gesendet wird.
Jede Anfrage enthält alle notwendigen Daten, um Änderungen in der Datenbank zu speichern. Da dataProcessor im REST-Modus eingerichtet ist, werden je nach Operationstyp unterschiedliche HTTP-Methoden verwendet.
Wenn Sie aus irgendeinem Grund die REST API nicht verwenden möchten, ist eine gute Alternative die Verwendung der dhtmlxConnector-Bibliothek.
Wiederkehrende Ereignisse werden in der Datenbank als Datensätze gespeichert, die alle Felder eines regulären Ereignisses sowie mehrere zusätzliche Felder enthalten: rrule, duration, recurring_event_id, original_start und deleted.
Weitere Details finden Sie im Artikel Wiederkehrende Ereignisse.
Neben diesen zusätzlichen Feldern benötigt der serverseitige Controller eine spezielle Logik:
Ein ausführliches Beispiel zum Bearbeiten und Löschen wiederkehrender Ereignisse finden Sie im entsprechenden Abschnitt des Artikels zu wiederkehrenden Ereignissen.
Falls der Scheduler zusätzliche Header an Ihr Backend senden soll, können Sie diese mit der Methode dataProcessor.setTransactionMode angeben.
Beispielsweise, um einen Autorisierungstoken zu Ihren Anfragen hinzuzufügen:
scheduler.init("scheduler_here");
scheduler.load("/api");
const dp = scheduler.createDataProcessor("/api");
dp.init(scheduler);
dp.setTransactionMode({
mode:"REST",
headers: {
"Authorization": "Token 9944b09199c62bcf9418ad846dd0e4bbdfc6ee4b"
}
});
Derzeit unterstützt load keine Header- oder Payload-Parameter für GET-Anfragen. Falls Sie diese benötigen, müssen Sie die xhr-Anfrage manuell senden und die Daten mit parse in den Scheduler laden, wie im folgenden Beispiel:
const authToken = '9944b09199c62bcf9418ad846dd0e4bbdfc6ee4b';
fetch("/api", {
method: "GET",
headers: {
"Content-Type": "application/json",
"Authorization": `Token ${authToken}`
}
})
.then(response => response.json())
.then(data => {
scheduler.parse(data);
})
.catch(error => {
console.error("Error:", error);
});
Es gibt mehrere Möglichkeiten, zusätzliche Parameter in Anfragen einzubinden.
Da der Scheduler alle Eigenschaften des Datenobjekts an das Backend sendet, können Sie einfach eine zusätzliche Eigenschaft direkt zum data object hinzufügen, und sie wird mitgesendet:
scheduler.attachEvent("onEventCreated", function(id,e){
const event = scheduler.getEvent(id);
event.userId = currentUser;
return true;
});
Alternativ können benutzerdefinierte Parameter zu allen von dataProcessor gesendeten Anfragen über die payload-Eigenschaft des setTransactionMode-Parameters hinzugefügt werden:
scheduler.init("gantt_here");
scheduler.load("/api");
const dp = scheduler.createDataProcessor("/api");
dp.init(scheduler);
dp.setTransactionMode({
mode:"REST",
payload: {
token: "9944b09199c62bcf9418ad846dd0e4bbdfc6ee4b"
}
});
Die Payload wird an die Query-String der Anfrage angehängt.
Eine weitere Möglichkeit ist die Verwendung des onBeforeUpdate-Events von DataProcessor:
const dp = scheduler.createDataProcessor("data/events.php");
dp.attachEvent("onBeforeUpdate", function(id, state, data){
data.productName = "Product 2";
return true;
});
Dieses Event wird für jeden Datensatz, der an das Backend gesendet wird, ausgelöst, und der benutzerdefinierte Parameter wird zu jedem Scheduler-Ereignis mit der Ereignis-ID als Präfix hinzugefügt, etwa so:
123_productName:Product 2
Sobald dataProcessor initialisiert ist, werden alle vom Benutzer oder per Code vorgenommenen Änderungen automatisch an die Datenquelle gesendet.
Um ein bestimmtes Ereignis programmatisch zu aktualisieren, wird in der Regel die Methode addEvent verwendet:
scheduler.parse([
{ id:1, start_date:"2017-05-13 6:00", end_date:"2017-05-13 8:00", text:"Event 1"},
{ id:2, start_date:"2017-06-09 6:00", end_date:"2017-06-09 8:00", text:"Event 2"}
]);
const event = scheduler.getEvent(1);
event.text = "Conference"; // ändert die Ereignisdaten
scheduler.addEvent(event); // zeigt das aktualisierte Ereignis an
Wird diese Methode für ein bereits geladenes Ereignis aufgerufen, löst addEvent eine update-Anfrage aus, andernfalls eine insert-Anfrage.
Diese Methoden senden Aktualisierungen an das Backend:
dhtmlxScheduler kann auch ohne gantt.dataProcessor verwendet werden. In diesem Fall müssen Sie alle im Scheduler vorgenommenen Änderungen manuell verfolgen und dann an Ihr Backend senden.
Folgende Events sollten dazu überwacht werden:
Wenn clientseitig ein neues Ereignis erstellt wird, erhält es zunächst eine temporäre ID, bis es eine permanente Datenbank-ID erhält.
Nachdem das neue Element in die Datenbank eingefügt wurde, sollten Sie die neue ID an den Client zurücksenden und das zugehörige Ereignis mit der Methode changeEventId aktualisieren:
// Angenommen, eventService ist eine CRUD-Service-Implementierung
scheduler.attachEvent('onEventAdded', function(id, event) {
eventService.create(event)
.then(function(result){
scheduler.changeEventId(id, result.databaseId);
});
});
scheduler.attachEvent('onEventChanged', function(id, event) {
eventService.update(event);
});
scheduler.attachEvent('onEventDeleted', function(id) {
eventService.delete(id);
});
Wenn das RESTful AJAX API nicht zu Ihren Backend-Anforderungen passt oder Sie manuell steuern möchten, was an den Server gesendet wird, können Sie ein eigenes Routing verwenden.
Beispielsweise verfügen Frameworks wie Angular oder React möglicherweise über Komponenten, die Änderungen nicht direkt an den Server senden, sondern an eine andere Komponente weitergeben, die für das Speichern der Daten verantwortlich ist.
Um ein eigenes Routing für DataProcessor einzurichten, verwenden Sie die Methode createDataProcessor():
const server = "/api";
scheduler.createDataProcessor(function(entity, action, data, id) {
switch(action) {
case "create":
return scheduler.ajax.post(
`${server}/${entity}`,
data
);
break;
case "update":
return scheduler.ajax.put(
`${server}/${entity}/${id}`,
data
);
break;
case "delete":
return scheduler.ajax.del(
`${server}/${entity}/${id}`
);
break;
}
});
Das Scheduler AJAX-Modul kann für eigenes Routing nützlich sein. Scheduler erwartet, dass ein eigener Router ein Promise zurückgibt, das das Ergebnis der Operation repräsentiert und so die Erkennung des Abschlusses ermöglicht.
Das AJAX-Modul unterstützt Promises und funktioniert gut innerhalb eigener Router. Scheduler verarbeitet das Promise, sobald es aufgelöst wurde.
Im Beispiel unten wird eine neue Aufgabe erstellt. Wenn die Serverantwort die ID der neu erstellten Aufgabe enthält, kann Scheduler diese entsprechend übernehmen.
scheduler.createDataProcessor(function(entity, action, data, id){
...
switch (action) {
case "create":
return scheduler.ajax.post({
headers: {
"Content-Type": "application/json"
},
url: server + "/" + entity + "/" + id,
data: JSON.stringify(data)
});
break;
}
});
Der Server kann den Scheduler über eine fehlgeschlagene Aktion informieren, indem er eine Antwort mit "action":"error" zurückgibt:
{"action":"error"}
Diese Antwort kann clientseitig mit dataProcessor behandelt werden:
const dp = scheduler.createDataProcessor("apiUrl");
dp.init(scheduler);
dp.attachEvent("onAfterUpdate", function(id, action, tid, response){
if(action === "error"){
// Fehlerbehandlung hier
}
});
Das Antwortobjekt kann zusätzliche Eigenschaften enthalten, die über das Argument response
im onAfterUpdate-Handler zugänglich sind.
Wenn der Server einen Fehler zurückgibt, die Änderungen aber clientseitig gespeichert wurden, empfiehlt es sich, die Clientdaten zu löschen und die korrekten Daten vom Server neu zu laden:
dp.attachEvent("onAfterUpdate", function(id, action, tid, response){
if(action === "error"){
scheduler.clearAll();
scheduler.load(url);
}
});
Wenn Sie nicht alle Daten neu laden möchten, können Sie ein einzelnes Ereignis nur clientseitig mit dem silent-Parameter der Methode deleteEvent entfernen:
// Entfernt das angegebene Ereignis nur clientseitig, ohne Serveraufruf
scheduler.deleteEvent(id, true);
Beachten Sie, dass Scheduler selbst keinen Schutz gegen Bedrohungen wie SQL-Injections, XSS oder CSRF-Angriffe bietet.
Die Sicherheit Ihrer Anwendung liegt in der Verantwortung der Backend-Entwickler.
Weitere Informationen zu möglichen Schwachstellen und wie Sie die Sicherheit Ihrer Anwendung verbessern können, finden Sie im Artikel Anwendungssicherheit.
Nach oben