服务端集成
将 dhtmlxGantt 连接到后端的推荐方法是在服务器上实现一个 RESTful API,并在客户端使用 模块。
DataProcessor 是一个内置模块,能够监控 Gantt 的数据变动并以指定格式将更新发送到 REST API,从而便于与 服务器端平台的集成。当使用对象数据源时,可以配置 DataProcessor 以提供数据变更的回调,您可以利用它进行数据绑定。
您可以查看一个视频指南,展示如何在页面上创建一个 Gantt 图并以 Node.js 平台为例将数据加载到其中。
技术要点
通常,要使用 REST API 从服务器端加载数据,您需要:
客户端
-
调用 方法,作为参数指定返回符合 JSON 格式的 Gantt 数据的 URL。
-
使用以下两种方式之一创建 DataProcessor 实例:
- 初始化 DataProcessor 并将其附加到 dhtmlxGantt 对象:
gantt.init("gantt_here");
gantt.load("apiUrl");
// 保持以下代码行的顺序
const dp = new gantt.dataProcessor("apiUrl");
dp.init(gantt);
dp.setTransactionMode("REST");
dp.deleteAfterConfirmation = true;
建议使用第二种方法。
const dp = gantt.createDataProcessor({
url: "apiUrl",
mode: "REST",
deleteAfterConfirmation: true
});
请在下一节中查看详细信息。
创建 DataProcessor
通过 API 方法 创建 DataProcessor 时,您在传参方面有几种可选项。
- 使用预定义的请求模式之一,如下所示:
const dp = gantt.createDataProcessor({
url: "/api",
mode: "REST",
deleteAfterConfirmation: true
});
其中:
- url - 服务器端的 URL
- mode - 发送数据到服务器的模式: "GET" | "POST" | "REST" | "JSON" | "REST-JSON"
- deleteAfterConfirmation - 定义是否只有在服务器返回成功响应后才从甘特图中删除该任务。依赖链接和子任务将在父任务删除确认后被删除。
- 提供一个自定义的 router 对象:
const dp = gantt.createDataProcessor(router);
- where router 要么是一个函数:
// entity - "task"|"link"|"resource"|"assignment"
// action - "create"|"update"|"delete"
// data - 包含任务或链接数据的对象
// id – 被处理对象(任务或链接)的 id
const dp = gantt.createDataProcessor((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;
}
});
- 或者是具备以下结构的对象:
const dp = gantt.createDataProcessor({
task: {
create: (data) => {},
update: (data, id) => {},
delete: (id) => {}
},
link: {
create: (data) => {},
update: (data, id) => {},
delete: (id) => {}
}
});
router 对 象的所有函数都应返回一个 Promise 或数据响应对象。这是 dataProcessor 应用数据库 ID 并 Hook data processor 的 onAfterUpdate 事件所必需的。
const router = (entity, action, data, id) => {
return new gantt.Promise((resolve, reject) => {
// … some logic
return resolve({ tid: databaseId });
});
};
因此,您可以使用 DataProcessor 将数据保存到 localStorage,或保存到任何其他与特定 URL 无关的存储,或者在存在两个不同服务器(URL)分别负责创建和删除对象的情况下使用。
相关示例: 自定义数据 API - 使用本地存储
请求与响应细节
URL 的格式规则如下:
- api/link/id
- api/task/id
- api/resource/id
- api/assignment/id
其中 "api" 是您在 dataProcessor 配置中指定的 URL。
可能的请求与响应列表为:
| Action | HTTP Method | URL | Response |
|---|---|---|---|
| load data | GET | /apiUrl | JSON format |
| Tasks | |||
| add a new task | POST | /apiUrl/task | ("action":"inserted","tid":"id") |
| update a task | PUT | /apiUrl/task/id | ("action":"updated") |
| delete a task | DELETE | /apiUrl/task/id | ("action":"deleted") |
| Links | |||
| add a new link | POST | /apiUrl/link | ("action":"inserted","tid":"id") |
| update a link | PUT | /apiUrl/link/id | ("action":"updated") |
| delete a link | DELETE | /apiUrl/link/id | ("action":"deleted") |
| Resources | |||
| add a new resource | POST | /apiUrl/resource | ("action":"inserted","tid":"id") |
| update a resource | PUT | /apiUrl/resource/id | ("action":"updated") |
| delete a resource | DELETE | /apiUrl/resource/id | ("action":"deleted") |
| Resource Assignments | |||
| add a new assignment | POST | /apiUrl/assignment | ("action":"inserted","tid":"id") |
| update an assignment | PUT | /apiUrl/assignment/id | ("action":"updated") |
| delete an assignment | DELETE | /apiUrl/assignment/id | ("action":"deleted") |
默认情况下,Resources 和 Resource Assignments 不会发送到 DataProcessor。如果需要,可以显式开启此行为。 请在这里阅读 guides/server-side.md#resources_crud。
请求参数
创建/更新/删除请求将包含客户端任务或链接对象的所有公共属性:
Task:
- start_date: 2025-04-08 00:00:00
- duration: 4
- text: Task #2.2
- parent: 3
- end_date: 2025-04-12 00:00:00
Link:
- source: 1
- target: 2
- type: 0
注释:
- start_date 和 end_date 参数的格式由 配置定义。
- 客户端会发送任务或链接对象的所有公共属性。因此,请求可能包含任意数量的附加参数。
- 如果通过向数据模型中添加新的列/属性来扩展数据模型,则不需要执行额外操作即可让 gantt 将它们发送到后端。
这里所说的公共属性指的是名称不以下划线 (_) 或美元符号 ($) 开头的属性,例如名为 task._owner 或 link.$state 的属性不会发送到后端。
REST-JSON 模式
除了 "POST","GET","REST" 和 "JSON" 交易模式之外,Gantt DataProcessor 还可以在 "REST-JSON" 模式下使用。
gantt.load("apiUrl");
const dp = gantt.createDataProcessor({
url: "/apiUrl",
mode: "REST-JSON"
});
它使用与 请求的 URL 相同的 URL,但任务和链接的 请求参数 以及发送到服务器的形式不同。
在 REST 模式中,数据以表单形式发送到服务器:
Content-Type: application/x-www-form-urlencoded
而在 REST-JSON 模式中,数据以 JSON 格式发送:
Content-type: application/json
因此,参数作为 JSON 对象发送:
- Task
{
"start_date": "20-09-2025 00:00",
"text": "New task",
"duration": 1,
"end_date": "21-09-2025 00:00",
"parent": 0,
"usage": [
{ "id": "1", "value": "30" },
{ "id": "2", "value": "20" }
]
}
- Link
{
"source": 1,
"target": 2,
"type": "0"
}
这种格式使在任何服务器端平台上处理复杂记录更加方便。
服务端
在甘特图执行的每个操作(添加、更新或删除任务或链接)时,dataProcessor 会通过向服务器发送 AJAX 请求来作出响应。
每个请求都包含在数据库中保存变更所需的所有数据。由于我们在 REST 模式下初始化 dataProcessor,因此它会为每种操作使用不同的 HTTP 动词。
由于使用 REST API,可以使用不同的框架和编程语言来实现服务端。下面是可用于 Gantt 后端集成的现成服务端实现列表:
- dhtmlxGantt with ASP.NET Core 2
- dhtmlxGantt with PHP: Slim
- dhtmlxGantt with PHP: Laravel
- dhtmlxGantt with Node.js
- dhtmlxGantt with ASP.NET MVC
- dhtmlxGantt with Ruby on Rails
存储任务顺序
Gantt 按数据源返回的顺序显示任务。如果您允许用户手动重新排序任务,您还需要在数据库中存储此顺序,并确保数据源返回的数据按正确顺序排序。
客户端配置:
// 在整个 gantt 内重新排序任务
gantt.config.order_branch = true;
gantt.config.order_branch_free = true;
gantt.init("gantt_here");
gantt.load("/api");
const dp = gantt.createDataProcessor({
url: "/api",
mode: "REST"
});
保存排序的实现方式有多种,我们将展示其中一种。
- 您在任务表中添加一个数值列,称之为 'sortorder'。
- 在处理 GET 操作时,按该列进行升序排序。
- 当新任务被添加时,应该将 sortorder 设置为
MAX(sortorder) + 1。 - 当客户端改变顺序时,gantt 将发送带有任务所有属性以及描述任务在项目树中位置的值的 PUT(如果不使用 REST 模式则为 POST)请求。
| HTTP 方法 | URL | 参数 | 响应 |
|---|---|---|---|
| PUT | /apiUrl/task/taskId | target=adjacentTaskId | ("action":"updated") |
target 参数包含当前任务紧邻的前一个或后一个任务的 id。
它的值可能以以下两种格式出现:
- target="targetId" - 当前任务应紧挨着 在 targetId 任务之前
- target="next:targetId" - 当前任务应紧挨着 在 targetId 任务之后
应用排序变更通常涉及更新多个任务,以下是实现的伪代码示例:
const target = request["target"];
const currentTaskId = request["id"];
let nextTask;
let targetTaskId;
// 获取相邻任务的 id,并检查更新的任务应在它之前还是之后
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 值
let targetOrder = targetTask.sortorder;
// 如果应在相邻任务之后,则应获得更大的 sortorder
if (nextTask) targetOrder++;
// 将应在更新任务之后的任务的 sortorder 提高
tasks.where(task => task.sortorder >= targetOrder)
.update(task => task.sortorder++);
// 并将更新任务的 sortorder 设置为新的值
currentTask.sortorder = targetOrder;
tasks.save(currentTask);
您可以查看某些服务器端平台上实现存储任务顺序的详细示例: plain PHP,Laravel, Node.js、ASP.NET Web API 与 Rails。
自定义请求头与参数
添加自定义请求头
您可以向后端发送额外的头信息。比如,假设需要在请求中添加授权令牌:
gantt.init("gantt_here");
gantt.load("/api");
const dp = gantt.createDataProcessor({
url: "/api",
mode:"REST",
headers: {
"Content-Type": "application/x-www-form-urlencoded",
"Authorization": "Token 9944b09199c62bcf9418ad846dd0e4bbdfc6ee4b"
}
});
当前, 尚不支持头部/负载参数,因此如果您需要在 GET 请求中使用它们,您将需要手动发送 xhr 并使用 将数据加载到 gantt 中,例如:
gantt.ajax.get({
url: "/api",
headers: {
"Authorization": "Token 9944b09199c62bcf9418ad846dd0e4bbdfc6ee4b"
}
}).then(xhr => {
gantt.parse(xhr.responseText);
});