dhtmlxGantt mit Salesforce LWC

Diese Anleitung behandelt den Prozess der Integration von dhtmlxGantt in eine Salesforce Lightning Web Component.

Wenn Sie mit einer anderen Technologie arbeiten, gibt es weitere Integrationsoptionen, die Sie erkunden können:

Für dieses Tutorial verwenden wir Salesforce CLI, um eine Lightning Web Component zu erstellen und in eine Organisation zu implementieren. Es könnte auch hilfreich sein, das Salesforce Extension Pack für Visual Studio Code zu installieren, wenn Sie mit Entwicklungsorganisationen arbeiten.

Der vollständige Quellcode für dieses Beispiel ist auf GitHub verfügbar.

Es gibt auch ein Video-Tutorial, das zeigt, wie man ein Gantt-Diagramm mit Salesforce LWC einrichtet.

Voraussetzungen

Stellen Sie sicher, dass Salesforce CLI installiert ist. Sie können dieser Anleitung für Installationsanweisungen folgen.


Schritt 1. Erstellen eines Projekts

Wenn Sie noch kein Entwicklerkonto haben, können Sie sich kostenlos registrieren. Eine Anleitung zur Einrichtung eines Kontos finden Sie hier.

Beginnen Sie mit der Aktivierung von Dev Hub. Verwenden Sie die Suchleiste auf der linken Seite, um Dev Hub zu finden und auszuwählen:

Aktivieren Sie im Einstellungsfenster Dev Hub aktivieren:

Erstellen Sie als Nächstes ein Basisverzeichnis für das Salesforce DX-Projekt:

$ mkdir ~/salesforce

Generieren Sie ein Salesforce DX-Projekt mit dem CLI:

$ cd ~/salesforce
$ sfdx project generate -n gantt-salesforce-app  
    target dir = C:\Users\User\salesforce
        create gantt-salesforce-app\config\project-scratch-def.json
        create gantt-salesforce-app\README.md
        create gantt-salesforce-app\sfdx-project.json
        create gantt-salesforce-app\.husky\pre-commit
        create gantt-salesforce-app\.vscode\extensions.json
        create gantt-salesforce-app\.vscode\launch.json
        create gantt-salesforce-app\.vscode\settings.json
        create gantt-salesforce-app\force-app\main\default\lwc\.eslintrc.json
        create gantt-salesforce-app\force-app\main\default\aura\.eslintrc.json
        create gantt-salesforce-app\scripts\soql\account.soql
        create gantt-salesforce-app\scripts\apex\hello.apex
        create gantt-salesforce-app\.eslintignore
        create gantt-salesforce-app\.forceignore
        create gantt-salesforce-app\.gitignore
        create gantt-salesforce-app\.prettierignore
        create gantt-salesforce-app\.prettierrc
        create gantt-salesforce-app\jest.config.js
        create gantt-salesforce-app\package.json

Wechseln Sie in den Projektordner:

$ cd gantt-salesforce-app

Schritt 2. Autorisierung

Autorisieren Sie eine Organisation mithilfe des Webserver-Flows. Weitere Details finden Sie hier:

$ sfdx org login web -d
 
Erfolgreich autorisiert ... mit Org-ID ...

Aktualisieren Sie die Projektkonfigurationsdatei (sfdx-project.json). Setzen Sie den Parameter sfdcLoginUrl auf Ihre "My Domain URL". Diese URL finden Sie auf der "My Domain"-Einrichtungsseite. Zum Beispiel:

gantt-salesforce-app/sfdx-project.json

"sfdcLoginUrl" : "https://xbs2-dev-ed.my.salesforce.com"

Erstellen Sie eine Scratch-Org:

$ sfdx org create scratch -f config/project-scratch-def.json -d
 
Scratch-Org wird erstellt...
RequestId: 2SR5j0000006JhCGAU 
(https://xbsoftware2-dev-ed.my.salesforce.com/2SR5j0000006JhCGAU)
OrgId: 00DH40000000s0D
Username: test-tc0telfqhudt@example.com
✓ Anfrage vorbereiten
✓ Anfrage senden
✓ Warten auf Org
✓ Verfügbar
✓ Authentifizieren
✓ Einstellungen bereitstellen
Fertig
 
Ihre Scratch-Org ist bereit.

Schritt 3. Hinzufügen von Gantt zu Salesforce

Um die Bibliothek zu verwenden, laden Sie sie als statische Ressource zu Salesforce hoch. Öffnen Sie zunächst Ihre Scratch-Org:

$ sfdx org open

Gehen Sie zur Registerkarte "Statische Ressourcen" und klicken Sie auf die Schaltfläche "Neu":

Weisen Sie einen aussagekräftigen Namen zu (z.B. "dhtmlxgantt7111"), laden Sie das ZIP-Archiv mit den Bibliotheksdateien hoch (dhtmlxgantt.js und dhtmlxgantt.css) und setzen Sie die "Cache-Steuerung" auf "Öffentlich" für eine bessere Leistung. Speichern Sie Ihre Änderungen:

Die dhtmlxGantt-Bibliothek ist jetzt in Salesforce verfügbar:


Schritt 4. Erstellen eines Datenmodells

Die Haupteinheiten für dhtmlxGantt sind Tasks und Links. Ein praktischer Ansatz besteht darin, alle Eigenschaften dieser Einheiten als einfaches JSON in Salesforce zu speichern. Beginnen Sie mit der Erstellung von Tasks- und Links-Objekten. Öffnen Sie den Objekt-Manager, klicken Sie auf "Erstellen" und wählen Sie dann "Benutzerdefiniertes Objekt":

Task-Objekt

Benennen Sie das Task-Objekt GanttTask/GanttTasks:

Stellen Sie sicher, dass der Datensatzname mit dem Objektnamen übereinstimmt. Zum Beispiel:

Objektname: GanttTask => Datensatzname: GanttTask Name

Speichern Sie Ihre Änderungen.

Nachdem das Objekt erstellt wurde, wechseln Sie zur Registerkarte "Felder & Beziehungen" und klicken Sie auf "Neu":

Felder hinzufügen

  • Dauer

    • Wählen Sie "Nummer" als Datentyp und fahren Sie mit dem nächsten Schritt fort:

    • Benennen Sie das Feld "Dauer". Es wird die JSON-serialisierten Task-Eigenschaften speichern. Klicken Sie weiter auf "Weiter", bis Sie "Speichern & Neu" auswählen können:

  • Eltern

    • Erstellen Sie ein Feld "Eltern" mit "Text" als Datentyp:

    • Fahren Sie mit den Standardeinstellungen fort, bis Sie "Speichern & Neu" auswählen können.
  • Fortschritt

    • Erstellen Sie ein Feld "Fortschritt" mit "Nummer" als Datentyp:

    • Fahren Sie mit den Standardeinstellungen fort, bis Sie "Speichern & Neu" auswählen können.
  • Startdatum

    • Erstellen Sie ein Feld "Startdatum" mit "Datum/Uhrzeit" als Datentyp:

    • Fahren Sie mit den Standardeinstellungen fort, bis Sie "Speichern" auswählen können.

Das Endergebnis sollte in etwa so aussehen:

Link-Objekt

Um zu beginnen, öffnen Sie den Objekt-Manager und wählen Sie "Erstellen", dann "Benutzerdefiniertes Objekt":

Benennen Sie das Link-Objekt als GanttLink/GanttLinks.

Stellen Sie sicher, dass der Datensatzname mit dem Objektnamen übereinstimmt. Zum Beispiel:

Objektname: GanttLink => Datensatzname: GanttLink Name

Erstellen Sie nun die erforderlichen Felder.

  • Quelle

Erstellen Sie ein Feld namens "Quelle" und wählen Sie "Text" als Datentyp.

Klicken Sie auf "Weiter" (unter Beibehaltung der Standardeinstellungen), bis die Schaltfläche "Speichern & Neu" erscheint.

  • Ziel

Erstellen Sie ein Feld namens "Ziel" und wählen Sie "Text" als Datentyp.

Klicken Sie auf "Weiter" (unter Beibehaltung der Standardeinstellungen), bis die Schaltfläche "Speichern & Neu" erscheint.

  • Typ

Erstellen Sie ein Feld namens "Typ" und wählen Sie "Text" als Datentyp.

Klicken Sie auf "Weiter" (unter Beibehaltung der Standardeinstellungen), bis die Schaltfläche "Speichern" erscheint.

Wenn Sie fertig sind, sollte es so aussehen:


Schritt 5. Erstellen einer Lightning Web Component

Um eine Lightning Web Component zu erstellen, verwenden Sie diesen Befehl:

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

Ändern Sie die Datei gantt.js-meta.xml, um sie im Lightning App Builder sichtbar zu machen:

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

<?xml version="1.0" encoding="UTF-8"?>
<LightningComponentBundle xmlns="http://soap.sforce.com/2006/04/metadata">
    <apiVersion>54.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>

Fügen Sie in gantt.html diesen Code hinzu:

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

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

Fügen Sie in gantt.js diesen Code hinzu:

force-app/main/default/lwc/gantt/gantt.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";
 
// Statische Ressourcen
import GanttFiles from "@salesforce/resourceUrl/dhtmlxgantt7111";
 
// Controller
import getTasks from "@salesforce/apex/GanttData.getTasks";
 
function unwrap(fromSF) {
    const data = fromSF.tasks.map((a) => ({
        id: a.Id,
        text: a.Name,
        start_date: a.Start_Date__c,
        duration: a.Duration__c,
        parent: a.Parent__c,
        progress: a.Progress__c,
        type: a.Task_Type__c,
    }));
    const links = fromSF.links.map((a) => ({
        id: a.Id,
        source: a.Source__c,
        target: a.Target__c,
        type: a.Type__c
    }));
    return { data, links};
}
 
export default class GanttView extends LightningElement {
    static delegatesFocus = true;
 
    @api height;
    ganttInitialized = false;
 
    renderedCallback() {
        if (this.ganttInitialized) {
            return;
        }
        this.ganttInitialized = true;
 
        Promise.all([
            loadScript(this, GanttFiles + "/dhtmlxgantt.js"),
            loadStyle(this, GanttFiles + "/dhtmlxgantt.css")
        ])
            .then(() => {
                this.initializeUI();
            })
            .catch((error) => {
                this.dispatchEvent(
                    new ShowToastEvent({
                        title: "Fehler beim Laden von Gantt",
                        message: error.message,
                        variant: "error"
                    })
                );
            });
    }
 
    initializeUI() {
        const root = this.template.querySelector(".thegantt");
        root.style.height = this.height + "px";
 
        //entfernen Sie die Kommentierung der folgenden Zeile, wenn Sie die Enterprise- oder Ultimate-Version verwenden
        //const gantt = window.Gantt.getGanttInstance();
        gantt.templates.parse_date = (date) => new Date(date);
        gantt.templates.format_date = (date) => date.toISOString();
 
        gantt.init(root);
        getTasks().then((d) => {
            const chartData = unwrap(d);
            gantt.parse({
                tasks: chartData.data,
                links: chartData.links
            });
        });
 
        ///↓↓↓ Änderungen zurück an das SF-Backend speichern ↓↓↓
        gantt.createDataProcessor({
            task: {
                create: (data) => {
                    console.log("createTask",data);
                    const insert = {
                        apiName: "GanttTask__c",
                        fields: {
                            Name: data.text,
                            Start_Date__c: data.start_date,
                            Duration__c: data.duration,
                            Parent__c: String(data.parent),
                            Progress__c: data.progress
                        }
                    };
                    gantt.config.readonly = true; // Änderungen unterdrücken
                                                  // bis das Speichern abgeschlossen ist  
                    return createRecord(insert).then((res) => {
                        gantt.config.readonly = false;
                        return { tid: res.id, ...res };
                    });
                },
                update: (data, id) => {
                    console.log("updateTask",data);
                    const update = {
                        fields: {
                            Id: id,
                            Name: data.text,
                            Start_Date__c: data.start_date,
                            Duration__c: data.duration,
                            Parent__c: String(data.parent),
                            Progress__c: data.progress
                        }
                    };
                    return updateRecord(update).then(() => ({}));
                },
                delete: (id) => {
                    return deleteRecord(id).then(() => ({}));
                }
            },
            link: {
                create: (data) => {
                    const insert = {
                        apiName: "GanttLink__c",
                        fields: {
                            Source__c: data.source,
                            Target__c: data.target,
                            Type__c: data.type
                        }
                    };
                    return createRecord(insert).then((res) => {
                        return { tid: res.id };
                    });
                },
                update: (data, id) => {
                    const update = {
                        apiName: "GanttLink__c",
                        fields: {
                            Id: id,
                            Source__c: data.source,
                            Target__c: data.target,
                            Type__c: data.type
                        }
                    };
                    return updateRecord(update).then(() => ({}));
                },
                delete: (id) => {
                    return deleteRecord(id).then(() => ({}));
                }
            }
        });
    }
}

Schritt 6. Erstellen einer Apex-Klasse

Erstellen Sie nun eine Klasse, um die Interaktionen zwischen der Lightning-Komponente und dem Datenmodell zu handhaben:

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

Öffnen Sie GanttData.cls und fügen Sie diesen Code hinzu:

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

public with sharing class GanttData {
 
    @RemoteAction
    @AuraEnabled(cacheable=true)
    public static Map<String, Object> getTasks() {
 
        // Abrufen der Datensätze über SOQL
        List<GanttTask__c> Tasks = new List<GanttTask__c>();
        Tasks = [SELECT Id, Name, Start_Date__c, Duration__c, 
                    Parent__c FROM GanttTask__c];
 
        List<GanttLink__c> Links = new List<GanttLink__c>();
        Links = [SELECT Id, Type__c, Source__c, Target__c FROM GanttLink__c];
 
        Map<String, Object> result = new Map<String, Object>{
            'tasks' => Tasks, 'links' => Links };
        return result;
   }
}

Rufen Sie den Quellcode von der Scratch-Org ab:

$ sfdx project retrieve start

Dann drücken Sie den Quellcode zurück in die Scratch-Org:

$ sfdx project deploy start

Schritt 7. Erstellen einer Lightning-Seite

Öffnen Sie den "Lightning App Builder" und erstellen Sie eine neue Lightning-Seite.

Wählen Sie "App-Seite", legen Sie dann den Seitennamen und das Layout fest.

Sie sollten nun die Gantt-Benutzerkomponente aufgelistet sehen. Fügen Sie sie zu einer Region hinzu und speichern Sie.

Aktivieren Sie die Seite.

Speichern Sie Ihre Änderungen.

Öffnen Sie die Anwendungsseite. Sie können sie im App-Launcher finden, indem Sie nach "Gantt" suchen.

Wenn alles korrekt eingerichtet ist, sehen Sie eine einfache Gantt-Demo, die auf der Lightning-Seite läuft.


Anwendungssicherheit

Die Gantt-Komponente enthält keinen eingebauten Schutz gegen Bedrohungen wie SQL-Injektionen, XSS oder CSRF-Angriffe. Es ist wichtig sicherzustellen, dass Ihre Anwendung sicher ist. Entwickler sind verantwortlich für die Implementierung geeigneter Sicherheitsmaßnahmen. Weitere Details finden Sie im entsprechenden Artikel. Salesforce bietet auch robuste Sicherheitsfunktionen, um Ihre Daten und Anwendungen zu schützen. Für zusätzliche Informationen lesen Sie den Salesforce Security Guide oder erkunden Sie den Secure Coding Guide.


Fehlerbehebung

Wenn Tasks und Links nach Abschluss der Einrichtung nicht auf der Seite erscheinen, werfen Sie einen Blick auf den Artikel Fehlerbehebung bei Backend-Integrationsproblemen. Er bietet Anleitungen zur Diagnose und Lösung potenzieller Probleme.


Was kommt als Nächstes

Jetzt haben Sie eine funktionierende Gantt-Komponente. Der vollständige Code ist auf GitHub verfügbar. Sie können ihn gerne klonen oder für Ihre Projekte herunterladen.

Sie können auch Anleitungen zu Gantt-Funktionen erkunden oder Tutorials zum Integrieren von Gantt mit anderen Backend-Frameworks durchsuchen.

Zurück nach oben