dhtmlxScheduler с SalesForce LWC
В этом руководстве описан процесс интеграции dhtmlxScheduler в SalesForce Lightning Web Component.
Посмотрите онлайн‑демо по теме интеграции компонентов DHTMLX с Salesforce LWC (Логин: user, Пароль: demo). Исходный код демо‑примера приведён на GitHub.
Если вы используете другую технологию, ниже приведён список доступных вариантов интеграции:
- dhtmlxScheduler с ASP.NET Core
- dhtmlxScheduler с ASP.NET MVC
- dhtmlxScheduler с PHP
- dhtmlxScheduler с PHP:Slim
- dhtmlxScheduler с PHP:Laravel
- dhtmlxScheduler с Ruby on Rails
- dhtmlxScheduler с dhtmlxConnector
Мы будем использовать SalesForce CLI для создания Lightning Web Component и загрузки его в организацию. См. эту статью для руководства по установке. Вы также можете установить Salesforce Extension Pack в Visual Studio Code для работы с разработческими организациями.
Полный исходный код доступен на GitHub.
Вы можете посмотреть видеоруководство, которое показывает, как создать Scheduler с Salesforce LWC.
Требования
Установите SalesForce CLI, если его нет. См. эту статью для руководства по установке.
Шаг 1. Создание проекта
Зарегистрируйтесь для бесплатной учетной записи разработчика, если у вас её нет. См. эту статью для инструкций по установке.
Слева в строке поиска найдите и выберите Dev Hub:

В новом окне настроек выберите Enable Dev Hub:

Давайте создадим базовый каталог для проекта Salesforce DX:
$ mkdir ~/salesforce
Создайте проект Salesforce DX через CLI:
$ cd ~/salesforce
$ sfdx project generate -n scheduler-salesforce-app
target dir = C:UsersUsersalesforce
create scheduler-salesforce-appconfigproject-scratch-def.json
create guides/scheduler-salesforce-appREADME.md
create scheduler-salesforce-appsfdx-project.json
create scheduler-salesforce-app.huskypre-commit
create scheduler-salesforce-app.vscodeextensions.json
create scheduler-salesforce-app.vscodelaunch.json
create scheduler-salesforce-app.vscodesettings.json
create scheduler-salesforce-appforce-appmaindefaultlwc.eslintrc.json
create scheduler-salesforce-appforce-appmaindefaultaura.eslintrc.json
create scheduler-salesforce-appscriptssoqlaccount.soql
create scheduler-salesforce-appscriptsapexhello.apex
create scheduler-salesforce-app.eslintignore
create scheduler-salesforce-app.forceignore
create scheduler-salesforce-app.gitignore
create scheduler-salesforce-app.prettierignore
create scheduler-salesforce-app.prettierrc
create scheduler-salesforce-appjest.config.js
create scheduler-salesforce-apppackage.json
Перейдите в созданный проект:
$ cd scheduler-salesforce-app
Шаг 2. Авторизация
Авторизуйте организацию с использованием потока Web Server:
$ sfdx org login web -d
Successfully authorized ...@...com with org ID ...
Обновите файл конфигурации проекта (sfdx-project.json). Установите параметр "sfdcLoginUrl" на ваш "My Domain URL". Найти ваш "My Domain URL" можно на странице настройки "My Domain". Например:

"sfdcLoginUrl" : "https://xbs2-dev-ed.my.salesforce.com"
Создайте Scratch Org:
$ sfdx org create scratch -f config/project-scratch-def.json -d
Creating Scratch Org...
RequestId: 2SR8a000000PLf5GAG (https://xbs2-dev-ed.my.salesforce.com/2SR8a000000PLf5GAG)
OrgId: 00D8G000000EEMs
Username: test-3baxo2k0tpej@example.com
✓ Prepare Request
✓ Send Request
✓ Wait For Org
✓ Available
✓ Authenticate
✓ Deploy Settings
Done
Your scratch org is ready.
Шаг 3. Добавление Scheduler в Salesforce
Чтобы начать использовать библиотеку, её нужно загрузить в Salesforce как Static Resource. Откройте ваш scratch org:
$ sfdx org open
Теперь перейдите на вкладку "Static Resources" и нажмите кнопку "New"

Дайте осмысленное имя (мы используем dhtmlxscheduler), выберите ZIP‑архив самой библиотеки (архив должен содержать файлы dhtmlxscheduler.js и dhtmlxscheduler.css), и выберите режим кэширования "Public" для повышен ия производительности. Нажмите кнопку "Save".

Теперь у нас внутри Salesforce есть dhtmlxScheduler.

Шаг 4. Создание модели данных
Основные сущности dhtmlxScheduler — это События. Хорошим подходом является хранение всех свойств сущностей dhtmlxScheduler в виде простого JSON внутри Salesforce. Давайте создадим объект Event. Откройте Object Manager и выберите "Create", затем "Custom Object":

Объект SchedulerEvent/SchedulerEvents
Дайте имя объекту события, пусть это будет SchedulerEvent/SchedulerEvents.

Имя записи должно соответствовать имени объекта, например:
Object Name: SchedulerEvent => Record Name: SchedulerEvent Name
Нажмите кнопку "Save".
После создания объекта откройте вкладку "Fields & Relationships". Нажмите кнопку "New".

- Start Date
Выберите "Date/Time" в качестве типа данных и нажмите кнопку "Next".

Назовите его "Start Date". Он хранит свойства задач в виде JSON.

Нажмите кнопку "Next" (принимая все значения по умолчанию) до тех пор, пока не станет доступна кнопка "Save & New".
- End Date
Создайте поле "End Date". Выберите "Date/Time" в качестве типа данных.

Нажмите кнопку "Next" (принимая все значения по умолчанию) до тех пор, пока не станет доступна кнопка "Save & New".
- Text
Создайте поле "Text". Выберите тип данных "Text".

Нажмите кнопку "Next" (принимая все значения по умолчанию) до тех пор, пока не станет доступна кнопка "Save".
В конце должно выглядеть так:

Шаг 5. Создание компонента Lightning Web
Чтобы создать компонент Lightning Web, выполните команду:
$ sfdx lightning generate component --type lwc -n scheduler -d force-app/main/default/lwc
target dir = C:UsersUsersourcesalesforcescheduler-salesforce-appforce-appmaindefaultlwc
create force-appmaindefaultlwcschedulerscheduler.js
create force-appmaindefaultlwcschedulerscheduler.html
create force-appmaindefaultlwcscheduler__tests__scheduler.test.js
create force-appmaindefaultlwcschedulerscheduler.js-meta.xml
Измените определение компонента в scheduler.js-meta.xml, чтобы сделать его доступным в Конструкторе приложений Lightning:
<?xml version="1.0" encoding="UTF-8"?>
<LightningComponentBundle xmlns="http://soap.sforce.com/2006/04/metadata">
<apiVersion>57.0</apiVersion>
<isExposed>true</isExposed>
<targets>
<target>lightning__AppPage</target>
</targets>
<targetConfigs>
<targetConfig targets="lightning__AppPage">
<property name="height" label="Height" type="Integer" default="800" />
</targetConfig>
</targetConfigs>
</LightningComponentBundle>
Откройте scheduler.html и добавьте следующий код в него:
<template>
<div className="thescheduler" lwc:dom="manual" style='width: 100%;'></div>
</template>
Откройте scheduler.js и добавьте следующий код в него:
/* eslint-disable guard-for-in */
/* eslint-disable no-undef */
import { LightningElement, api } from "lwc";
import { ShowToastEvent } from "lightning/platformShowToastEvent";
import { loadStyle, loadScript } from "lightning/platformResourceLoader";
import { createRecord, updateRecord, deleteRecord } from "lightning/uiRecordApi";
// Static resources
import SchedulerFiles from "@salesforce/resourceUrl/dhtmlxscheduler";
// Controllers
import getEvents from "@salesforce/apex/SchedulerData.getEvents";
function unwrap(fromSF) {
const data = fromSF.events.map((a) => ({
id: a.Id,
info: a.Name,
start_date: a.Start_Date__c,
end_date: a.End_Date__c,
text: a.Text__c,
}));
return { data };
}
export default class SchedulerView extends LightningElement {
static delegatesFocus = true;
@api height;
schedulerInitialized = false;
renderedCallback() {
if (this.schedulerInitialized) {
return;
}
this.schedulerInitialized = true;
Promise.all([
loadScript(this, SchedulerFiles + "/dhtmlxscheduler.js"),
loadStyle(this, SchedulerFiles + "/dhtmlxscheduler.css")
])
.then(() => {
this.initializeUI();
})
.catch((error) => {
this.dispatchEvent(
new ShowToastEvent({
title: "Error loading scheduler",
message: error.message,
variant: "error"
})
);
});
}
initializeUI() {
const root = this.template.querySelector(".thescheduler");
root.style.height = this.height + "px";
const scheduler = window.Scheduler.getSchedulerInstance();
scheduler.templates.parse_date = (date) => new Date(date);
scheduler.templates.format_date = (date) => date.toISOString();
scheduler.config.header = [
"day",
"week",
"month",
"date",
"prev",
"today",
"next"
];
scheduler.init(root, new Date(), "week");
getEvents().then((d) => {
const chartData = unwrap(d);
scheduler.parse({
events: chartData.data,
});
});
///↓↓↓ saving changes back to SF backend ↓↓↓
scheduler.createDataProcessor(function (entity, action, data, id) {
switch (action) {
case "create":
console.log("createEvent", data);
const insert = {
apiName: "SchedulerEvent__c",
fields: {
Name: data.info,
Start_Date__c: data.start_date,
End_Date__c: data.end_date,
Text__c: data.text
}
};
scheduler.config.readonly = true; // suppress changes
//until saving is complete
return createRecord(insert).then((res) => {
scheduler.config.readonly = false;
return { tid: res.id, ...res };
});
case "update":
console.log("updateEvent", data);
const update = {
fields: {
Id: id,
Name: data.info,
Start_Date__c: data.start_date,
End_Date__c: data.end_date,
Text__c: data.text
}
};
return updateRecord(update).then(() => ({}));
case "delete":
return deleteRecord(id).then(() => ({}));
}
});
}
}