dhtmlxScheduler с SalesForce LWC

В этом руководстве описан процесс интеграции dhtmlxScheduler в SalesForce Lightning Web Component.

Если вы работаете с другой технологией, ознакомьтесь с другими вариантами интеграции, приведёнными ниже:

Мы будем использовать SalesForce CLI для создания Lightning Web Component и его деплоя в вашу организацию. Для информации по установке обратитесь к этой статье. Дополнительно вы можете установить Salesforce Extension Pack для Visual Studio Code, чтобы упростить работу с организациями для разработки.

Полный исходный код доступен на GitHub.

Ниже приведён видеоурок, демонстрирующий создание Scheduler с использованием Salesforce LWC.

Необходимые условия

Убедитесь, что у вас установлен SalesForce CLI. Если нет — ознакомьтесь с этой статьёй для получения инструкций по установке.

Шаг 1. Создание проекта

Если у вас ещё нет аккаунта, зарегистрируйтесь для получения бесплатного аккаунта разработчика. Эта статья поможет вам в этом.

Используйте строку поиска слева, чтобы найти и выбрать Dev Hub:

Затем в открывшемся окне настроек включите Dev Hub:

Далее создайте базовую папку для вашего проекта Salesforce DX:

$ mkdir ~/salesforce

Сгенерируйте проект Salesforce DX с помощью CLI:

$ cd ~/salesforce
$ sfdx project generate -n scheduler-salesforce-app
    target dir = C:\Users\User\salesforce
        create scheduler-salesforce-app\config\project-scratch-def.json
        create scheduler-salesforce-app\README.md
        create scheduler-salesforce-app\sfdx-project.json
        create scheduler-salesforce-app\.husky\pre-commit
        create scheduler-salesforce-app\.vscode\extensions.json
        create scheduler-salesforce-app\.vscode\launch.json
        create scheduler-salesforce-app\.vscode\settings.json
        create scheduler-salesforce-app\force-app\main\default\lwc\.eslintrc.json
        create scheduler-salesforce-app\force-app\main\default\aura\.eslintrc.json
        create scheduler-salesforce-app\scripts\soql\account.soql
        create scheduler-salesforce-app\scripts\apex\hello.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-app\jest.config.js
        create scheduler-salesforce-app\package.json

Перейдите в папку с новым проектом:

$ cd scheduler-salesforce-app

Шаг 2. Авторизация

Авторизуйте свою организацию с помощью Web Server Flow, выполнив команду:

$ sfdx org login web -d
 
Successfully authorized ...@...com with org ID ...

Далее обновите файл конфигурации проекта (sfdx-project.json), установив параметр "sfdcLoginUrl" в значение вашего "My Domain URL". Найти этот URL можно на странице настроек "My Domain" вашей организации. Например:

scheduler-salesforce-app/sfdx-project.json

"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) и установите Cache Control в "Public" для повышения производительности. Нажмите "Save".

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

Шаг 4. Создание модели данных

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

Объект события

Назовите объект события SchedulerEvent или SchedulerEvents.

Убедитесь, что имя записи совпадает с именем объекта, например:

Имя объекта: SchedulerEvent => Имя записи: SchedulerEvent Name

Нажмите кнопку “Сохранить”.

После создания объекта перейдите на вкладку "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 Component

Чтобы создать Lightning Web Component, используйте следующую команду:

$ sfdx lightning generate component --type lwc -n scheduler -d force-app/main/default/lwc
 
target dir = C:\Users\User\source\salesforce\scheduler-salesforce-app\force-app\main\default\lwc
   create force-app\main\default\lwc\scheduler\scheduler.js
   create force-app\main\default\lwc\scheduler\scheduler.html
   create force-app\main\default\lwc\scheduler\__tests__\scheduler.test.js
   create force-app\main\default\lwc\scheduler\scheduler.js-meta.xml

Измените определение компонента в scheduler.js-meta.xml, чтобы сделать его доступным в Lightning App Builder:

force-app/main/default/lwc/scheduler/scheduler.js-meta.xml

<?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 и вставьте следующий код:

force-app/main/default/lwc/scheduler/scheduler.html

<template>
    <div class="thescheduler" lwc:dom="manual" style='width: 100%;'></div>
</template>

Далее откройте scheduler.js и добавьте следующий код:

force-app/main/default/lwc/scheduler/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(() => ({}));
            }
        });
    }
}

Шаг 6. Создание Apex класса

Следующий шаг — создать класс, который позволит взаимодействовать между Lightning Component и моделью данных.

$ sfdx apex generate class -n SchedulerData -d force-app/main/default/classes
 
target dir = C:\Users\User\salesforce\scheduler-salesforce-app\force-app\main\default\classes
   create force-app\main\default\classes\SchedulerData.cls
   create force-app\main\default\classes\SchedulerData.cls-meta.xml

После создания откройте SchedulerData.cls и добавьте следующий код:

force-app/main/default/classes/SchedulerData.cls

public with sharing class SchedulerData {
 
    @RemoteAction
    @AuraEnabled(cacheable=true)
    public static Map<String, Object> getEvents() {
 
        // fetching the Records via SOQL
        List<SchedulerEvent__c> Events = new List<SchedulerEvent__c>();
        Events = [SELECT Id, Name, Start_Date__c, End_Date__c, 
            Text__c FROM SchedulerEvent__c];
 
        Map<String, Object> result = new Map<String, Object>{'events' => Events };
        return result;
   }
}

Получите исходный код из Scratch Org в ваш проект:

$ sfdx project retrieve start

Затем отправьте исходный код обратно в Scratch Org:

$ sfdx project deploy start

Шаг 7. Создание Lightning Page

Откройте "Lightning App Builder" и создайте новую Lightning Page.

Выберите "App Page", затем введите имя страницы и выберите макет.

Пользовательский компонент Scheduler должен быть доступен для новой страницы. Добавьте его в любую область и сохраните.

Активируйте страницу.

Сохраните изменения.

Откройте страницу приложения. Она должна быть доступна в лаунчере приложений по запросу Scheduler.

Если всё настроено правильно, на Lightning Page отобразится простая демонстрация Scheduler.

Безопасность приложения

Scheduler сам по себе не содержит встроенной защиты от угроз, таких как SQL-инъекции, XSS или CSRF-атаки. Обеспечение безопасности приложения лежит на разработчиках. Подробнее см. в соответствующей статье. Salesforce спроектирован с учетом безопасности для защиты ваших данных и приложений, и вы можете реализовать собственные меры безопасности, подходящие вашей организации. Дополнительную информацию смотрите в Salesforce Security Guide. Кроме того, этот ресурс содержит важные рекомендации по безопасности.

Устранение неполадок

Если Scheduler не отображает события после завершения интеграции, обратитесь к статье Troubleshooting Backend Integration Issues. В ней содержатся рекомендации по выявлению и устранению распространённых проблем.

Что дальше

На этом этапе Scheduler полностью работоспособен. Полный исходный код доступен на GitHub для клонирования или загрузки для использования в ваших проектах.

Вы также можете ознакомиться с руководствами по возможностям Scheduler или с учебными материалами по интеграции Scheduler с другими backend-фреймворками.

Наверх