将 dhtmlxScheduler 与后端连接的最佳方式是在服务器端设置 RESTful API,并在客户端使用 dhtmlxDataProcessor。
DataProcessor 是包含在 dhtmlxScheduler.js 中的客户端库。它负责跟踪数据的变更并管理来自客户端的服务器请求。
你可以通过 REST API 将 dhtmlxScheduler 与各种框架和编程语言的服务器端集成。以下是 Scheduler 后端集成可用的服务器端实现列表:
通常,要通过 REST API 从服务器加载数据,你需要:
1) 使用 load 方法,指定返回 Scheduler 数据的 JSON 格式的 URL。
2) 使用 createDataProcessor 方法,并传递包含配置选项的对象:
const dp = scheduler.createDataProcessor({
url: "apiUrl",
mode: "REST"
});
另外,你也可以使用构造函数创建 dataProcessor,并将其附加到 dhtmlxScheduler 对象。scheduler.DataProcessor() 构造函数接受服务器端脚本的路径:
scheduler.init("scheduler_here", new Date(), "month");
scheduler.load("apiUrl");
const dp = new scheduler.DataProcessor("apiUrl");
dp.init(scheduler);
更详细的信息请参见下节。
通过 API 方法 createDataProcessor 创建 DataProcessor 时,你有多种方式传递参数。
1. 使用预定义的请求模式之一,例如:
const dp = scheduler.createDataProcessor({
url: "/api",
mode: "REST"
});
其中:
2. 提供自定义的 router 对象:
const dp = scheduler.createDataProcessor(router);
router 可以是一个函数:
const server = "/api";
// entity - "event"
// action - "create"|"update"|"delete"
// data - 包含事件数据的对象
// id – 被处理对象(事件)的 id
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;
}
});
或是如下结构的对象:
const dp = scheduler.createDataProcessor({
event: {
create: function(data) {},
update: function(data, id) {},
delete: function(id) {}
}
});
所有 router 函数都应返回 Promise 或数据响应对象。这样 dataProcessor 可以分配数据库 id 并触发 onAfterUpdate 事件。
const router = function(entity, action, data, id) {
return new Promise(function(resolve, reject) {
// … 业务逻辑
return resolve({tid: databaseId});
});
}
通过这种方式,DataProcessor 可用于将数据保存到 localStorage 或任何不绑定到特定 URL 的存储,或者当对象的创建和删除由不同服务器(URL)处理时使用。
URL 遵循如下模式:
其中 "api" 是 dataProcessor 配置中指定的 URL。
要使用 REST 模式,请将 createDataProcessor 配置对象的 mode
属性设置为 "REST":
const dp = scheduler.createDataProcessor({
url: "apiUrl",
mode: "REST"
});
典型的请求与响应如下:
操作 | HTTP 方法 | URL | 响应 |
---|---|---|---|
加载数据 | GET | /apiUrl | JSON 格式 |
新增事件 | POST | /apiUrl | {"action":"inserted","tid":"eventId"} |
更新事件 | PUT | /apiUrl/:id | {"action":"updated"} |
删除事件 | DELETE | /apiUrl/:id | {"action":"deleted"} |
要使用 REST-JSON 模式,请将 createDataProcessor 配置对象的 mode
属性设置为 "REST-JSON":
const dp = scheduler.createDataProcessor({
url: "apiUrl",
mode: "REST-JSON"
});
在此模式下,scheduler 以 application/json
content type 发送 POST/PUT/DELETE 请求。
请求与响应如下:
操作 | HTTP 方法 | URL | 请求体 | 响应 |
---|---|---|---|---|
加载数据 | GET | /apiUrl | JSON 格式 | |
新增事件 | POST | /apiUrl | { "start_date":"2019-12-18 00:00", "end_date":"2019-12-18 00:05", "text":"New event", ... } |
{ "action":"inserted", "tid":"eventId" } |
更新事件 | PUT | /apiUrl/:id | { "start_date":"2024-12-18 00:00", "end_date":"2024-12-18 00:05", "text":"New event", ... } |
{"action":"updated"} |
删除事件 | DELETE | /apiUrl/:id | {"action":"deleted"} |
要使用 POST 模式,请将 createDataProcessor 配置对象的 mode
属性设置为 "POST":
const dp = scheduler.createDataProcessor({
url: "apiUrl",
mode: "POST"
});
请求与响应如下:
操作 | HTTP 方法 | URL | 响应 |
---|---|---|---|
加载数据 | GET | /apiUrl | JSON 格式 |
更新事件 | POST | /apiUrl | {"action":"inserted|updated|deleted", "tid":"eventId"} |
要使用 JSON 模式,请将 createDataProcessor 配置对象的 mode
属性设置为 "JSON":
const dp = scheduler.createDataProcessor({
url: "apiUrl",
mode: "JSON"
});
在此模式下,scheduler 在每次数据更新后向服务器发送 POST 请求(类似 POST 模式,但请求格式不同)。
请求与响应如下:
操作 | HTTP 方法 | 请求体 | 响应 |
---|---|---|---|
加载数据 | GET | JSON 格式 | |
新增事件 | 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" } |
更新事件 | 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"} |
删除事件 | 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"} |
动态加载的请求与响应如下:
操作 | HTTP 方法 | URL | 响应 |
---|---|---|---|
加载数据 | GET | /apiUrl?from=minDate&to=maxDate | JSON 格式 |
创建/更新/删除请求包含客户端事件对象的所有公共属性:
!nativeeditor_status 参数仅适用于 POST 模式。
每当在 Scheduler 中执行操作(如添加、更新或删除事件)时,dataProcessor 会通过发送 AJAX 请求与服务器进行交互。
每个请求都携带了保存数据库更改所需的所有数据。由于 dataProcessor 被设置为 REST 模式,因此会根据操作类型使用不同的 HTTP 动词。
如果您因任何原因不想使用 REST API,可以选择 使用 dhtmlxConnector 库 作为替代方案。
循环事件在数据库中以记录的形式保存,这些记录包含了常规事件的所有字段,并额外包含以下字段:rrule、duration、recurring_event_id、original_start 和 deleted。
您可以在 循环事件 文章中找到更多详细信息。
除了这些额外字段外,服务器端控制器还需实现如下特定逻辑:
您可以在 循环事件相关章节 查看编辑和删除循环事件的详细示例。
如果需要 Scheduler 向后端发送额外的请求头,可以通过 dataProcessor.setTransactionMode 方法进行设置。
例如,向请求中添加授权 token:
scheduler.init("scheduler_here");
scheduler.load("/api");
const dp = scheduler.createDataProcessor("/api");
dp.init(scheduler);
dp.setTransactionMode({
mode:"REST",
headers: {
"Authorization": "Token 9944b09199c62bcf9418ad846dd0e4bbdfc6ee4b"
}
});
目前,load 不支持 GET 请求的 header 或 payload 参数,因此如有需要,您需手动发送 xhr 请求,并使用 parse 将数据加载到 scheduler,如下:
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);
});
有多种方式可以在请求中包含额外参数。
由于 scheduler 会将数据对象的所有属性发送到后端,您可以直接在 data object 上添加额外属性,它就会被一并发送:
scheduler.attachEvent("onEventCreated", function(id,e){
const event = scheduler.getEvent(id);
event.userId = currentUser;
return true;
});
另外,也可以通过 setTransactionMode 方法的 payload 属性,为 dataProcessor 发送的所有请求添加自定义参数:
scheduler.init("gantt_here");
scheduler.load("/api");
const dp = scheduler.createDataProcessor("/api");
dp.init(scheduler);
dp.setTransactionMode({
mode:"REST",
payload: {
token: "9944b09199c62bcf9418ad846dd0e4bbdfc6ee4b"
}
});
payload 会被追加到请求的查询字符串中。
另一种方式是使用 DataProcessor 的 onBeforeUpdate 事件:
const dp = scheduler.createDataProcessor("data/events.php");
dp.attachEvent("onBeforeUpdate", function(id, state, data){
data.productName = "Product 2";
return true;
});
此事件会在每条记录发送到后端前触发,自定义参数会以事件 id 作为前缀添加到每个 Scheduler 事件中,如下:
123_productName:Product 2
一旦初始化了 dataProcessor,用户通过界面或代码做的任何更改都会自动保存到数据源。
若要以编程方式更新指定事件,通常使用 addEvent 方法:
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"; // 修改事件数据
scheduler.addEvent(event); // 显示已更新的事件
当对已加载到 scheduler 的事件调用该方法时,addEvent 会触发 update 请求;否则会触发 insert。
以下方法会将更新发送到后端:
dhtmlxScheduler 也可以在不使用 gantt.dataProcessor 的情况下使用。在这种情况下,您需要手动跟踪 scheduler 中的所有更改,并将其发送到后端。
需监听如下事件:
在客户端新建事件时,最初会分配一个临时 id,直到获得数据库分配的正式 id。
将新项插入数据库后,应将新 id 返回给客户端,并通过 changeEventId 方法更新相关事件:
// 假设 eventService 是某种 CRUD 服务实现
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);
});
如果 RESTful AJAX API 不适合您的后端需求,或者您希望手动控制发送到服务器的内容,可以使用自定义路由。
例如,Angular 或 React 等框架的组件可能不会直接将更改发送到服务器,而是传递给另一个负责保存数据的组件。
要为 DataProcessor 设置自定义路由,请使用 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;
}
});
Scheduler AJAX 模块 对于自定义路由非常有用。Scheduler 期望自定义路由返回一个 Promise,用于表示操作结果,以便检测操作是否完成。
AJAX 模块支持 Promise,并可在自定义路由中良好工作。Scheduler 会在 Promise 被解析后进行处理。
以下示例中,创建了一个新任务。如果服务器响应中包含新任务的 id,Scheduler 可以相应地进行应用。
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;
}
});
服务器可以通过返回 "action":"error" 的响应通知 Scheduler 操作失败:
{"action":"error"}
可以在客户端通过 dataProcessor 进行处理:
const dp = scheduler.createDataProcessor("apiUrl");
dp.init(scheduler);
dp.attachEvent("onAfterUpdate", function(id, action, tid, response){
if(action === "error"){
// 在此处理错误
}
});
响应对象可能包含其他属性,可以通过 onAfterUpdate 处理函数的 response
参数访问。
如果服务器返回错误但客户端已保存更改,最佳做法是清空客户端数据并从服务器重新加载正确数据:
dp.attachEvent("onAfterUpdate", function(id, action, tid, response){
if(action === "error"){
scheduler.clearAll();
scheduler.load(url);
}
});
如果不希望重新加载所有数据,也可以仅从客户端删除单个事件,使用 deleteEvent 方法的 silent 参数:
// 仅从客户端移除指定事件,不与服务器交互
scheduler.deleteEvent(id, true);
请注意,Scheduler 本身不提供针对 SQL 注入、XSS 或 CSRF 攻击等威胁的保护。
确保应用安全是后端开发者的责任。
有关潜在漏洞及如何提升应用安全性的更多信息,请参阅 应用安全 文章。
返回顶部