Die beste Methode, um dhtmlxGantt mit einem Backend zu verknüpfen, besteht darin, eine RESTful API auf dem Server einzurichten und dhtmlxDataProcessor auf der Clientseite zu verwenden. Die DataProcessor-Bibliothek, die mit dhtmlxGantt.js geliefert wird, verfolgt Datenänderungen und bearbeitet Serveranfragen auf der Clientseite.
Gantt arbeitet mit seiner eigenen Version von DataProcessor, die einige einzigartige Merkmale im Vergleich zur Standardbibliothek aufweist. Diese Anleitung erklärt, wie der Gantt DataProcessor für die serverseitige Plattformintegration verwendet wird.
Für ein praktisches Beispiel sehen Sie sich das Video-Tutorial an, das zeigt, wie ein Gantt-Diagramm auf einer Seite eingerichtet und Daten darin geladen werden, wobei eine Node.js-Plattform als Beispiel dient.
Um Daten über eine REST API vom Backend zu laden, sind typischerweise folgende Schritte erforderlich:
1) Verwenden Sie die load
Methode, um Gantt-Daten zu laden. Geben Sie die URL an, die die Daten im JSON-Format als Parameter zurückgibt.
2) Um eine DataProcessor-Instanz einzurichten, können Sie dies auf eine von zwei Arten tun:
gantt.init("gantt_here");
gantt.load("apiUrl");
// Stellen Sie sicher, dass die folgenden Zeilen in der richtigen Reihenfolge sind
var dp = new gantt.dataProcessor("apiUrl");
dp.init(gantt);
dp.setTransactionMode("REST");
dp.deleteAfterConfirmation = true;
createDataProcessor
Methode und Übergabe eines Objekts mit Konfigurationsoptionen:var dp = gantt.createDataProcessor({
url: "apiUrl",
mode: "REST",
deleteAfterConfirmation: true
});
Für weitere Details, schauen Sie in den nächsten Abschnitt.
Bei der Verwendung der createDataProcessor
API-Methode zum Erstellen eines DataProcessor haben Sie mehrere Konfigurationsoptionen zur Auswahl:
var dp = gantt.createDataProcessor({
url: "/api",
mode: "REST",
deleteAfterConfirmation: true
});
Hier ist, was die Eigenschaften bedeuten:
var dp = gantt.createDataProcessor(router);
Der router kann entweder eine Funktion sein:
// entity - "task"|"link"|"resource"|"assignment"
// action - "create"|"update"|"delete"
// data - task oder link Datenobjekt
// id – die ID des verarbeiteten Objekts (task oder link)
var dp = gantt.createDataProcessor(function(entity, action, data, id) {
switch(action) {
case "create":
return gantt.ajax.post(
server + "/" + entity,
data
);
break;
case "update":
return gantt.ajax.put(
server + "/" + entity + "/" + id,
data
);
break;
case "delete":
return gantt.ajax.del(
server + "/" + entity + "/" + id
);
break;
}
});
Oder ein Objekt mit der folgenden Struktur:
var dp = gantt.createDataProcessor({
task: {
create: function(data) {},
update: function(data, id) {},
delete: function(id) {}
},
link: {
create: function(data) {},
update: function(data, id) {},
delete: function(id) {}
}
});
Alle router-Objektfunktionen müssen ein Promise oder ein Datenantwortobjekt zurückgeben. Dies ermöglicht es DataProcessor, die Datenbank-ID anzuwenden und das onAfterUpdate-Event auszulösen.
router = function(entity, action, data, id) {
return new gantt.Promise(function(resolve, reject) {
// Führen Sie einige Logik aus
return resolve({tid: databaseId});
});
}
Dieser Ansatz ermöglicht es Ihnen, Daten in localStorage, anderen benutzerdefinierten Speicherlösungen oder sogar dann zu speichern, wenn separate Server die Erstellung und Löschung von Objekten verwalten.
Related sample: Custom data api - using local storage
URLs folgen diesem Muster:
Hierbei entspricht "api" der URL in der DataProcessor-Konfiguration.
Die möglichen Anfragen und Antworten sind unten aufgeführt:
Aktion | HTTP-Methode | URL | Antwort |
---|---|---|---|
Daten laden | GET | /apiUrl | JSON-Format |
Aufgaben | |||
Eine Aufgabe hinzufügen | POST | /apiUrl/task | {"action":"inserted","tid":"id"} |
Eine Aufgabe aktualisieren | PUT | /apiUrl/task/id | {"action":"updated"} |
Eine Aufgabe löschen | DELETE | /apiUrl/task/id | {"action":"deleted"} |
Links | |||
Einen Link hinzufügen | POST | /apiUrl/link | {"action":"inserted","tid":"id"} |
Einen Link aktualisieren | PUT | /apiUrl/link/id | {"action":"updated"} |
Einen Link löschen | DELETE | /apiUrl/link/id | {"action":"deleted"} |
Ressourcen | |||
Eine Ressource hinzufügen | POST | /apiUrl/resource | {"action":"inserted","tid":"id"} |
Eine Ressource aktualisieren | PUT | /apiUrl/resource/id | {"action":"updated"} |
Eine Ressource löschen | DELETE | /apiUrl/resource/id | {"action":"deleted"} |
Ressourcenzuweisungen | |||
Eine Zuweisung hinzufügen | POST | /apiUrl/assignment | {"action":"inserted","tid":"id"} |
Eine Zuweisung aktualisieren | PUT | /apiUrl/assignment/id | {"action":"updated"} |
Eine Zuweisung löschen | DELETE | /apiUrl/assignment/id | {"action":"deleted"} |
Standardmäßig werden Ressourcen und Ressourcenzuweisungen nicht an den DataProcessor gesendet. Um dies zu aktivieren, müssen Sie es explizit konfigurieren. Weitere Details finden Sie hier.
Erstellungs-/Aktualisierungs-/Löschanfragen enthalten alle öffentlichen Eigenschaften eines Aufgaben- oder Linkobjekts von der Clientseite:
Aufgabe:
Link:
Hinweis:
date_format
Konfiguration ab._
) oder einem Dollarzeichen ($
) beginnen. Zum Beispiel werden Eigenschaften wie task._owner oder link.$state nicht an das Backend gesendet.Der Gantt DataProcessor unterstützt den "REST-JSON"-Modus zusätzlich zu den "POST", "GET", "REST" und "JSON" Transaktionsmodi.
gantt.load("apiUrl");
var dp = gantt.createDataProcessor({
url: "/apiUrl",
mode: "REST-JSON"
});
Dieser Modus verwendet die gleichen URLs für Anfragen, aber die Anfrageparameter für Aufgaben und Links sowie deren Versand sind unterschiedlich.
Im REST-Modus werden Daten als Formular gesendet:
Content-Type: application/x-www-form-urlencoded
Im REST-JSON-Modus werden Daten im JSON-Format gesendet:
Headers
Content-type: application/json
Parameter werden als JSON-Objekt gesendet:
Anfrage-Payload
{
"start_date": "20-09-2018 00:00",
"text": "Neue Aufgabe",
"duration":1,
"end_date": "21-09-2018 00:00",
"parent": 0,
"usage":[{
{"id":"1", "value":"30"},
{"id":"2", "value":"20"}
}]
}
{
"source": 1,
"target": 2,
"type": "0"
}
Dieses Format vereinfacht die Verarbeitung komplexer Datensätze auf verschiedenen serverseitigen Plattformen.
Wenn Aufgaben oder Links in Gantt hinzugefügt, aktualisiert oder gelöscht werden, sendet der DataProcessor AJAX-Anfragen an den Server. Diese Anfragen tragen alle notwendigen Daten, um die Datenbank zu aktualisieren.
Im REST-Modus verwendet der DataProcessor verschiedene HTTP-Verben für jede Operation. Da REST-APIs weit verbreitet unterstützt werden, können Sie die Serverseite mit verschiedenen Frameworks und Programmiersprachen implementieren. Verfügbare serverseitige Implementierungen für Gantt umfassen:
Aufgaben in Gantt werden in der gleichen Reihenfolge angezeigt, wie sie aus der Datenquelle kommen. Wenn Benutzer Aufgaben manuell neu anordnen können, müssen Sie diese Reihenfolge in der Datenbank speichern und sicherstellen, dass der Datenfeed Aufgaben in der richtigen Reihenfolge zurückgibt.
Clientseitige Konfiguration:
// Erlauben Sie das Neuanordnen von Aufgaben innerhalb der Gantt-Struktur
gantt.config.order_branch = true;
gantt.config.order_branch_free = true;
gantt.init("gantt_here");
gantt.load("/api");
var dp = gantt.createDataProcessor({
url: "/api",
mode: "REST"
});
Eine Möglichkeit, die Reihenfolge zu speichern, besteht darin, eine numerische Spalte, wie 'sortorder', zur Aufgabentabelle hinzuzufügen:
MAX(sortorder) + 1
zu.HTTP-Methode | URL | Parameter | Antwort |
---|---|---|---|
PUT | /apiUrl/task/taskId |
|
{"action":"updated"} |
Der target-Parameter gibt die ID der nächstgelegenen Aufgabe an, entweder vor oder nach der aktuellen Aufgabe.
Formate für target:
Das Neuanordnen von Aufgaben erfordert häufig das Aktualisieren mehrerer Aufgaben. Hier ist ein Pseudo-Code-Beispiel:
const target = request["target"];
const currentTaskId = request["id"];
let nextTask;
let targetTaskId;
// Bestimmen, ob die Aufgabe vor oder nach der benachbarten Aufgabe geht
if(target.startsWith("next:")){
targetTaskId = target.substr("next:".length);
nextTask = true;
}else{
targetTaskId = target;
nextTask = false;
}
const currentTask = tasks.getById(currentTaskId);
const targetTask = tasks.getById(targetTaskId);
if(!targetTaskId)
return;
// Sortorder basierend auf der benachbarten Aufgabe zuweisen
let targetOrder = targetTask.sortorder;
if(nextTask)
targetOrder++;
// Sortorder für Aufgaben aktualisieren, die der aktualisierten Aufgabe folgen
tasks.where(task => task.sortorder >= targetOrder).
update(task => task.sortorder++);
// Die Aufgabe mit ihrem neuen Sortorder aktualisieren
currentTask.sortorder = targetOrder;
tasks.save(currentTask);
Für detaillierte Beispiele zum Speichern der Aufgabenreihenfolge auf spezifischen serverseitigen Plattformen, siehe diese Ressourcen: plain PHP, Laravel, Node.js, ASP.NET Web API, und Rails.
Wenn es erforderlich ist, zusätzliche Header in Anfragen von Gantt an Ihr Backend einzufügen, können Sie die Methode dataProcessor.setTransactionMode verwenden.
Hier ist ein schnelles Beispiel, wie Sie ein Autorisierungstoken zu Ihren Anfragen hinzufügen:
gantt.init("gantt_here");
gantt.load("/api");
var dp = gantt.createDataProcessor({
url: "/api",
mode:"REST",
headers: {
"Content-Type": "application/x-www-form-urlencoded",
"Authorization": "Token 9944b09199c62bcf9418ad846dd0e4bbdfc6ee4b"
}
});
Derzeit erlaubt die load
Methode keine Header oder Payload-Parameter in GET-Anfragen. Wenn Sie diese Funktionalität benötigen, können Sie das XHR manuell behandeln und die Daten dann mit der parse
Methode in Gantt laden. Hier ist ein Beispiel:
gantt.ajax.get({
url: "/api",
headers: {
"Authorization": "Token 9944b09199c62bcf9418ad846dd0e4bbdfc6ee4b"
}
}).then(function (xhr) {
gantt.parse(xhr.responseText)
});
Es gibt mehrere Möglichkeiten, zusätzliche Parameter in Ihre Anfragen einzufügen.
Eine einfache Möglichkeit besteht darin, eine neue Eigenschaft direkt zum Datenobjekt hinzuzufügen. Gantt sendet diese Eigenschaft automatisch an das Backend:
gantt.attachEvent("onTaskCreated", function(task){
task.userId = currentUser;
return true;
});
Sie können auch benutzerdefinierte Parameter in alle Anfragen des DataProcessors einfügen, indem Sie die payload-Eigenschaft in der setTransactionMode Methode verwenden:
gantt.init("gantt_here");
gantt.load("/api");
var dp = gantt.createDataProcessor({
url: "/api",
mode:"REST",
payload: {
token: "9944b09199c62bcf9418ad846dd0e4bbdfc6ee4b"
}
});
Eine weitere Möglichkeit, benutzerdefinierte Parameter hinzuzufügen, ist die Verwendung des onBeforeUpdate Events des DataProcessors:
var dp = gantt.createDataProcessor({
url: "/api",
mode:"REST"
});
dp.attachEvent("onBeforeUpdate", function(id, state, data){
data.projectId = "1";
return true;
});
Um die Backend-URL dynamisch zu ändern, können Sie die dataProcessor.url Methode verwenden:
dp.url("/url");
Wenn der DataProcessor initialisiert ist, werden alle vom Benutzer oder programmatisch vorgenommenen Änderungen automatisch mit der Datenquelle synchronisiert.
Um eine bestimmte Aufgabe oder Abhängigkeit programmgesteuert zu aktualisieren, können Sie die Methoden updateTask
oder updateLink
verwenden:
gantt.parse([
{id:1, start_date:"2019-05-13 6:00", end_date:"2019-05-13 8:00", text:"Event 1"},
{id:2, start_date:"2019-06-09 6:00", end_date:"2019-06-09 8:00", text:"Event 2"}
],"json");
gantt.getTask(1).text = "Task 111"; // Aufgabendaten aktualisieren
gantt.updateTask(1); // Aktualisierte Aufgabe rendern
Andere Methoden, die Updates an das Backend auslösen, umfassen:
Wenn die RESTful AJAX API nicht Ihren Backend-Anforderungen entspricht oder wenn Sie mehr Kontrolle über die an den Server gesendeten Daten benötigen, kann benutzerdefiniertes Routing implementiert werden.
Dies ist nützlich in Setups wie Angular oder React, wo Änderungen nicht direkt an den Server gesendet werden, sondern durch eine Komponente gehen, die für das Speichern der Daten verantwortlich ist.
Um benutzerdefiniertes Routing für den DataProcessor zu konfigurieren, verwenden Sie die createDataProcessor() Methode:
gantt.createDataProcessor(function(entity, action, data, id){
const services = {
"task": this.taskService,
"link": this.linkService
};
const service = services[entity];
switch (action) {
case "update":
return service.update(data);
case "create":
return service.insert(data);
case "delete":
return service.remove(id);
}
});
Related sample: Custom data api - using local storage
Das Gantt AJAX-Modul kann helfen, benutzerdefinierte Routen einzurichten. Es ermöglicht Gantt, Promise-Objekte für Operationen zu verarbeiten, wodurch sichergestellt wird, dass Aktionen ausgeführt werden, wenn das Promise gelöst wird.
Hier ist ein Beispiel, bei dem eine neue Aufgabe erstellt wird. Wenn die Serverantwort die ID der neuen Aufgabe enthält, wird Gantt sie anwenden:
gantt.createDataProcessor(function(entity, action, data, id){
...
switch (action) {
case "create":
return gantt.ajax.post({
headers: {
"Content-Type": "application/json"
},
url: server + "/task",
data: JSON.stringify(data)
});
break;
}
});
Ab Version 8.0 können Ressourcenzuweisungen als einzelne Einträge mit persistenten IDs an den DataProcessor gesendet werden, was die Integration von Backend-APIs erleichtert. Ressourcenänderungen können auch auf diese Weise gesendet werden.
Diese Funktion ist standardmäßig deaktiviert. Standardmäßig werden nur Aufgaben- und Linkänderungen an den DataProcessor gesendet. Um dies zu aktivieren, konfigurieren Sie Folgendes:
gantt.config.resources = {
dataprocessor_assignments: true,
dataprocessor_resources: true,
};
Mit dieser Einstellung werden Ressourcen und Zuweisungen als separate Anfragen gesendet, wenn der REST-Modus verwendet wird. Im benutzerdefinierten Routing-Modus können Sie diese Änderungen im Prozessor behandeln:
gantt.createDataProcessor({
task: {
create: (data) => {
return createRecord({type: "task", ...data}).then((res) => {
return { tid: res.id, ...res };
});
},
update: (data, id) => {
return updateRecord({type: "task", ...data}).then(() => ({}));
},
delete: (id) => {
return deleteRecord({type: "task:", id: id}).then(() => ({}));
}
},
link: {
create: (data) => {
...
},
update: (data, id) => {
...
},
delete: (id) => {
...
}
},
assignment: {
create: (data) => {
...
},
update: (data, id) => {
...
},
delete: (id) => {
...
}
},
resource: {
create: (data) => {
...
},
update: (data, id) => {
...
},
delete: (id) => {
...
}
}
});
Alternativ können Sie eine Funktionsdeklaration verwenden:
gantt.createDataProcessor(function(entity, action, data, id){
switch (entity) {
case "task":
break;
case "link":
break;
case "resource":
break;
case "assignment":
break;
}
});
Wenn der Server für eine Aktion einen Fehler zurückgibt, kann er Gantt mit einer Antwort wie dieser benachrichtigen:
{"action":"error"}
Sie können solche Fehler auf der Clientseite mit gantt.dataProcessor
behandeln:
var dp = gantt.createDataProcessor({
url: "/api",
mode:"REST"
});
dp.attachEvent("onAfterUpdate", function(id, action, tid, response){
if(action == "error"){
// Fehler behandeln
}
});
Das response
-Objekt kann zusätzliche Eigenschaften enthalten, die im onAfterUpdate
-Handler zugänglich sind.
Dieses Event behandelt nur verwaltete Fehler, die eine JSON-Antwort wie die oben gezeigte zurückgeben.
Für HTTP-Fehler beachten Sie das onAjaxError
API-Event.
Wenn der serverseitige Fehler eine Diskrepanz mit dem Client-seitigen Status verursacht, können Sie den Client-Status löschen und frische Daten vom Server neu laden:
dp.attachEvent("onAfterUpdate", function(id, action, tid, response){
if(action == "error"){
gantt.clearAll();
gantt.load("url1");
}
});
Um den Client-Server-Status ohne zusätzliche Serveraufrufe zu synchronisieren, können Sie die silent() Methode verwenden. Dies verhindert interne Events oder Serveraufrufe:
gantt.silent(function(){
gantt.deleteTask(item.id);
});
gantt.render();
Wenn eine Aufgabe gelöscht wird, werden standardmäßig auch ihre verschachtelten Aufgaben und zugehörigen Links entfernt. Gantt sendet für jedes entfernte Element eine separate Löschanfrage. Dies hilft, die Datenintegrität ohne Backend-Beteiligung aufrechtzuerhalten.
Wenn dies jedoch zu vielen AJAX-Aufrufen führt, können Sie die kaskadierende Löschung mit der cascade_delete
Konfiguration deaktivieren. In diesem Fall wird nur das oberste Element gelöscht, und das Backend wird erwartet, die zugehörigen Löschungen zu behandeln.
Gantt enthält keinen eingebauten Schutz gegen Bedrohungen wie SQL-Injektionen, XSS oder CSRF-Angriffe. Es liegt in der Verantwortung der Entwickler, ihre Anwendungen zu sichern.
Für weitere Details zu potenziellen Schwachstellen und Möglichkeiten zur Verbesserung der Sicherheit, beachten Sie den Artikel Anwendungssicherheit
.