dhtmlxGantt mit Ruby on Rails
In diesem Artikel lernst du, wie man ein Gantt-Diagramm mit einem Ruby on Rails Backend erstellt. Für die Implementierung dieser App verwenden wir Ruby 2.4.1, Rails 5.1.3 und MySQL. Diese Anleitung setzt voraus, dass alle Voraussetzungen bereits installiert sind. Andernfalls besuchen Sie bitte zuerst die offiziellen Tutorials.
Falls du eine andere Technologie verwendest, findest du unten die Liste der verfügbaren Integrationsvarianten:
- dhtmlxGantt mit ASP.NET Core
- dhtmlxGantt mit ASP.NET MVC
- dhtmlxGantt mit Node.js
- dhtmlxGantt mit Python
- dhtmlxGantt mit PHP: Laravel
- dhtmlxGantt mit PHP:Slim
- dhtmlxGantt mit Salesforce LWC
Werfen Sie einen Blick auf die Demo auf GitHub.
Schritt 1. Ein Projekt erstellen
Um ein neues Projekt hinzuzufügen, führe im Terminal einfach den folgenden Befehl aus:
rails new gantt-app -d mysql
Schritt 2. Gantt zur Seite hinzufügen
Lass uns zunächst mit der Erstellung eines Controllers und einer Standardseite für unsere Anwendung beginnen. Wechsel in das Anwendungsverzeichnis und generiere einen neuen Controller mit der index-Aktion:
cd gantt-app
rails generate controller gantt index
Die Ausgabe sollte bestätigen, dass neue Dateien erstellt wurden.
Festlegen einer Standardroute
Zur Konfiguration der Routen öffne die Datei config/routes.rb. Ändere die Standardroute auf die "index"-Aktion unseres neuen Controllers:
Rails.application.routes.draw do
root :to => "gantt#index"
end
Daraufhin können wir unseren Server testen, indem wir ihn in der Befehlszeile starten:
rails server
Öffne http://localhost:3000/ in deinem Browser. Das Ergebnis sollte wie folgt aussehen:

Also läuft die App und wir haben unsere Standardseite. Nun können wir fortfahren und ein Gantt-Diagramm hinzufügen.
Gantt zur Ansicht hinzufügen
Nun sind wir bereit, ein Gantt-Diagramm zu unserer Seite hinzuzufügen.
Öffne die Layout-Datei und füge einen Yield in das head-Tag ein. Wir verwenden ihn, um dhtmlxgantt-Dateien in die Seite einzubinden:
<!DOCTYPE html>
<html>
<head>
<title>dhtmlxGantt</title>
(= stylesheet_link_tag 'application', media:'all','data-turbolinks-track' => true )
(= javascript_include_tag 'application', 'data-turbolinks-track' => true )
(= yield(:head) ) /*!*/
(= csrf_meta_tags )
</head>
<body>
(= yield )
</body>
</html>
Danach wechseln Sie zur gantt/index-View und fügen dort ein Gantt-Diagramm hinzu:
( content_for :head do )
(= stylesheet_link_tag 'https://cdn.dhtmlx.com/gantt/edge/dhtmlxgantt.css' )
(= javascript_include_tag 'https://cdn.dhtmlx.com/gantt/edge/dhtmlxgantt.js' )
( end )
<div id="gantt_here" style='width:100%; height:800px;'></div>
<script>
gantt.init("gantt_here");
</script>
Beachte, dass wir die dhtmlx Gantt-Dateien vom [CDN] anstelle von lokal hinzugefügt haben. Für die Entwicklung möchtest du eine lesbare Version des Quellcodes verwenden, die dem Downloadpaket beiliegt.
Danach können wir uns das aktuelle Ergebnis ansehen. Öffne http://localhost:3000/ (den Rails-Server) in deinem Browser. Du solltest das folgende Ergebnis sehen:

Damit hast du ein Gantt-Diagramm, in dem du Aufgaben hinzufügen und ändern kannst. Es fehlt jedoch noch an der Speichermöglichkeit. Um das bereitzustellen, müssen wir mit der Erstellung von Modellen fortfahren.
Schritt 3. Modelle erstellen
Da wir MySQL verwenden, stelle sicher, dass du die richtigen Verbindungsparameter in config/database.yml konfiguriert hast, zum Beispiel:
development:
adapter: mysql2
encoding: utf8
host: localhost
database: gantt-app
username: root
password:
Jetzt müssen wir Modelle für Tasks und Links erstellen.
Um ein Modell für Tasks zu erstellen, müssen wir einen Befehl ausführen, der die Eigenschaften der Aufgabe enthält:
rails generate model Task
text:string
start_date:datetime
duration:integer
parent:integer
progress:decimal
Ein ähnlicher, aber kürzerer Befehl wird verwendet, um ein Modell für Links zu erstellen:
rails generate model Link
source:integer
target:integer
link_type:string:limit1
Beachte, dass das dhtmlxgantt-Linkobjekt eine Eigenschaft mit dem Namen type haben muss, die den Typ der Beziehung speichert (start-to-start, finish-to-finish, etc.).
Wir können eine solche Eigenschaft nicht zu unserem Modell hinzufügen, da der Name "type" bereits von ActiveRecord reserviert ist. Als Workaround nennen wir diese Eigenschaft link_type und führen die erforderliche Zuordnung im Controller durch.
Du kannst dir die vollständige Liste der Eigenschaften, sowohl Pflicht- als auch optionale, ansehen, die für das Task object und Link object verfügbar sind.
Nach dem das erledigt ist, müssen wir eine Migration ausführen, um unsere Datenbank zu aktualisieren:
rake db:migrate
Lass uns hier noch etwas Testdaten hinzufügen:
- Öffne die Rails-Konsole durch Ausführung von:
rails c
- Füge ein paar Aufgaben und Links wie folgt hinzu:
Task.create :text=>"Task 1", :start_date=>"2015-10-25", :duration=>2, :progress=>0;
Task.create :text=>"Task 2", :start_date=>"2015-10-27", :duration=>3, :progress=>0.5;
Link.create :source=>1, :target=>2, :link_type=>"0";
- Gib "exit" ein, um die Konsole zu schließen.
Als Nächstes müssen wir das Laden und Speichern von Daten im Diagramm mithilfe von Controllern implementieren.
Schritt 4. Laden von Daten
Nachdem wir Modelklassen erstellt und die Migration durchgeführt haben, können wir die Daten aus der Datenbank in unseren Gantt laden.
dhtmlxGantt erwartet Daten im JSON-Format. Zunächst fügen wir daher eine neue Aktion zu unserem GanttController hinzu, in der wir die Gantt-Daten lesen, formatieren und ausgeben:
class GanttController < ApplicationController
def index
end
def data
tasks = Task.all
links = Link.all
render :json=>{
:data => tasks.map{|task|{
:id => task.id,
:text => task.text,
:start_date => task.start_date.to_formatted_s(:db),
:duration => task.duration,
:progress => task.progress,
:parent => task.parent,
:open => true
}},
:links => links.map{|link|{
:id => link.id,
:source => link.source,
:target => link.target,
:type => link.link_type
}}
}
end
end
Füge eine Route für diese Aktion in routes.rb ein:
Rails.application.routes.draw do
root :to => "gantt#index"
scope '/api' do
get "/data", :to => "gantt#data"
end
end
Und rufe diese Aktion von der Client-Seite aus mit der Methode gantt.load:
gantt.config.date_format = "%Y-%m-%d %H:%i:%s";/*!*/
gantt.init("gantt_here");
gantt.load("/api/data");/*!*/
Beachte, dass die date_format Konfiguration das Format der Datumsangaben (das start_date der Task) vom Server festlegt.
Wenn du den Server jetzt startest und http://localhost:3000/ in deinem Browser öffnest, solltest du in der Lage sein, ein Gantt-Diagramm zu sehen, das mit Aufgaben und Links aus der Datenbank gefüllt ist. Allerdings würden keine Änderungen an die Datenbank zurückgeschickt. Wir werden dies im nächsten Schritt beheben.
Schritt 5. Änderungen speichern
dhtmlxGantt kann alle vom Benutzer vorgenommenen Änderungen an die REST-API auf dem Backend übertragen, wo alles in der Datenbank gespeichert werden kann. Du kannst die Details des Protokolls hier einsehen.
So werden wir nun das Speichern von Daten implementieren:
Zuerst aktivieren wir das Senden von Änderungen auf dem Client:
gantt.config.date_format = "%Y-%m-%d %H:%i:%s";
gantt.init("gantt_here");
gantt.load("/api/data");
var dp = new gantt.dataProcessor("/api");/*!*/
dp.init(gantt);/*!*/
dp.setTransactionMode("REST");/*!*/
Dann müssen zwei Controller hinzugefügt werden: einer für Tasks und einer für Links, und alle benötigten Aktionen implementieren.
Task-Controller erstellen
Lass uns mit einem Controller für Tasks beginnen:
rails generate controller task --no-helper --no-assets --no-view-specs
Da dieser Controller keine Views enthalten wird, haben wir die Flags --no-* verwendet, um keine unnötigen Dateien zu erzeugen.
Als Nächstes implementieren wir Aktionen zum Erstellen, Aktualisieren und Löschen von Tasks:
class TaskController < ApplicationController
protect_from_forgery
def update
task = Task.find(params["id"])
task.text = params["text"]
task.start_date = params["start_date"]
task.duration = params["duration"]
task.progress = params["progress"] || 0
task.parent = params["parent"]
task.save
render :json => {:action => "updated"}
end
def add
task = Task.create(
:text => params["text"],
:start_date=> params["start_date"],
:duration => params["duration"],
:progress => params["progress"] || 0,
:parent => params["parent"]
)
render :json => {:action => "inserted", :tid => task.id}
end
def delete
Task.find(params["id"]).destroy
render :json => {:action => "deleted"}
end
end
Ein paar Hinweise zu diesem Code:
- Wir benötigen keine get-Aktion, da alle Daten bereits von gantt#data geladen werden.
- Die Eigenschaft progress ist möglicherweise standardmäßig nicht auf dem Client initialisiert, daher müssen wir hier den Standardwert bereitstellen. Alternativ hätte man den Standardwert auch in der Modelklasse definieren können (z. B. mittels Migration).
- Eine Aktion, die einen neuen Eintrag erstellt, sollte die Datenbank-ID des neu eingefügten Datensatzes an den Client zurückgeben.
Nach diesem Schritt müssen wir die neuen Routen in der Config hinzufügen, damit Benutzer Tasks in unserem Gantt-Diagramm anzeigen, erstellen, aktualisieren und löschen können:
Rails.application.routes.draw do
root :to => "gantt#index"
scope '/api' do
get "/data", :to => "gantt#data"
post "/task", :to => "task#add"
put "/task/:id", :to => "task#update"
delete "/task/:id", :to => "task#delete"
end
end
Nun dasselbe für Links.
Link-Controller erstellen
Generiere einen Link-Controller:
rails generate controller link --no-helper --no-assets --no-view-specs
Die Implementierung könnte wie folgt aussehen:
class LinkController < ApplicationController
protect_from_forgery
def update
link = Link.find(params["id"])
link.source = params["source"]
link.target = params["target"]
link.link_type = params["type"]
link.save
render :json => {:action => "updated"}
end
def add
link = Link.create(
:source => params["source"],
:target => params["target"],
:link_type => params["type"]
)
render :json => {:action => "inserted", :tid => link.id}
end
def delete
Link.find(params["id"]).destroy
render :json => {:action => "deleted"}
end
end
Dann füge Routen für neue Aktionen hinzu:
Rails.application.routes.draw do
root :to => "gantt#index"
scope '/api' do
get "/data", :to => "gantt#data"
post "/task", :to => "task#add"
put "/task/:id", :to => "task#update"
delete "/task/:id", :to => "task#delete"
post "/link", :to => "link#add"/*!*/
put "/link/:id", :to => "link#update"/*!*/
delete "/link/:id", :to => "link#delete"/*!*/
end
end
Und das war’s. Wenn du die Anwendung jetzt ausführst, erhältst du ein interaktives Gantt-Diagramm mit Rails- und MySql-Backend:

Bitte schau dir weitere unserer Guides an, um mehr Funktionen von dhtmlxGantt kennenzulernen.
Speichern der Reihenfolge der Aufgaben
Der clientseitige Gantt erlaubt das Neuordnen von Aufgaben per Drag & Drop. Wenn du diese Funktion verwendest, musst du diese Reihenfolge in der Datenbank speichern. Du kannst hier die allgemeine Beschreibung finden.
Lass uns diese Funktion nun zu unserer App hinzufügen.
Das Neuanordnen von Aufgaben im Client aktivieren
Zuerst müssen wir es Benutzern ermöglichen, die Reihenfolge der Aufgaben in der UI zu ändern. Öffne die Index-Ansicht und aktualisiere die Konfiguration des gantt:
gantt.config.order_branch = true;/*!*/
gantt.config.order_branch_free = true;/*!*/
gantt.init("gantt_here");
Nun spiegeln wir diese Änderungen im Backend wider. Wir müssen die Sortierinformation in unserem Modell speichern, wir nennen sie sortorder. Die aktualisierte Modell-Definition könnte so aussehen:
rails generate model Task
text:string
start_date:datetime
duration:integer
parent:integer
progress:decimal
sortorder:integer /*!*/
Oder füge eine neue Eigenschaft zum bestehenden Modell hinzu:
- Eine Migration erstellen:
rails generate migration add_sortorder_to_tasks sortorder:integer
- Öffne die generierte Migration und füge dem Feld "sortorder" einen Standardwert hinzu:
class AddSortorderToTasks < ActiveRecord::Migration[5.1]
def change
add_column :tasks, :sortorder, :integer, :default=>0
end
end
Und migriere:
rake db:migrate
Danach müssen wir CRUD im Controller aktualisieren:
- Die data-Aktion muss Aufgaben nach der Spalte
sortordersortiert zurückgeben:
class GanttController < ApplicationController
def index
end
def data
tasks = Task.all
links = Link.all
render :json=>{
:data => tasks.order(:sortorder).map{|task|{ /*!*/
:id => task.id,
:text => task.text,
:start_date => task.start_date.to_formatted_s(:db),
:duration => task.duration,
:progress => task.progress,
:parent => task.parent,
:open => true
}},
:links => links.map{|link|{
:id => link.id,
:source => link.source,
:target => link.target,
:type => link.link_type
}}
}
end
end
- Neu hinzugefügte Aufgaben müssen den anfänglichen Wert
sortordererhalten:
class TaskController < ApplicationController
...
def add
maxOrder = Task.maximum("sortorder") || 0/*!*/
task = Task.create(
:text => params["text"],
:start_date=> params["start_date"],
:duration => params["duration"],
:progress => params["progress"] || 0,
:parent => params["parent"],
:sortorder => maxOrder + 1/*!*/
)
render :json => {:action => "inserted", :tid => task.id}
end
end
- Schließlich müssen, wenn ein Benutzer Aufgaben neu anordnet, die Aufgabenreihenfolgen aktualisiert werden:
class TaskController < ApplicationController
protect_from_forgery
def update
task = Task.find(params["id"])
task.text = params["text"]
task.start_date = params["start_date"]
task.duration = params["duration"]
task.progress = params["progress"] || 0
task.parent = params["parent"]
task.save
if(params['target'])/*!*/
Task.updateOrder(task.id, params['target'])/*!*/
end/*!*/
render :json => {:action => "updated"}
end
...
end
Task.updateOrder-Implementierung:
class Task < ApplicationRecord
def self.updateOrder(taskId, target)
nextTask = false
targetId = target
if(target.start_with?('next:'))
targetId = target['next:'.length, target.length]
nextTask = true;
end
if(targetId == 'null')
return
end
targetTask = self.find(targetId)
targetOrder = targetTask.sortorder
if(nextTask)
targetOrder += 1
end
self.where("sortorder >= ?", targetOrder).
update_all('sortorder = sortorder + 1')
task = self.find(taskId)
task.sortorder = targetOrder
task.save
end
end
Anwendungssicherheit
Gantt bietet keine Mittel, um eine Anwendung vor verschiedenen Bedrohungen zu schützen, wie SQL-Injektionen oder XSS- und CSRF-Angriffe. Es ist wichtig, dass die Verantwortung für die Sicherheit der Anwendung beim Entwickler liegt, der das Backend implementiert. Die Details finden Sie im entsprechenden Artikel.
Fehlerbehebung
Falls du die oben beschriebenen Schritte zur Integration von Gantt mit Ruby on Rails abgeschlossen hast, Gantt aber keine Aufgaben und Links auf einer Seite rendert, schau dir den Artikel Fehlerbehebung bei der Backend-Integration an. Er beschreibt die Methoden zur Identifizierung der Ursachen der Probleme.
Was ist als Nächstes
Nun hast du ein voll funktionsfähiges Gantt. Den vollständigen Code kannst du auf GitHub einsehen, klonen oder herunterladen und für deine Projekte verwenden.
Du kannst auch unsere Guides zu den zahlreichen Funktionen von gantt oder Tutorials zur Integration von Gantt mit anderen Backend-Frameworks einsehen.