dhtmlxGantt mit Ruby on Rails
Dieser Artikel beschreibt die Erstellung eines Gantt-Diagramms mit einem Ruby on Rails Backend. Das Beispiel verwendet Ruby 2.4.1, Rails 5.1.3 und MySQL. Es wird vorausgesetzt, dass diese Voraussetzungen bereits installiert sind. Falls nicht, sollten Sie sich zunächst die offiziellen Tutorials ansehen.
Falls Sie mit einem anderen Technologie-Stack arbeiten, finden Sie weitere Integrationsmöglichkeiten hier:
- dhtmlxGantt with ASP.NET Core
- dhtmlxGantt with ASP.NET MVC
- dhtmlxGantt with Node.js
- dhtmlxGantt with Python
- dhtmlxGantt with PHP: Laravel
- dhtmlxGantt with PHP:Slim
- dhtmlxGantt with Salesforce LWC
Eine Demo ist ebenfalls auf GitHub verfügbar: https://github.com/DHTMLX/gantt-howto-rails.
Schritt 1. Projekt erstellen
Um ein neues Projekt zu erstellen, führen Sie folgenden Befehl im Terminal aus:
rails new gantt-app -d mysql
Schritt 2. Gantt zur Seite hinzufügen
Erstellen Sie zunächst einen Controller und die Standardseite für die App. Navigieren Sie in Ihr Anwendungsverzeichnis und generieren Sie einen neuen Controller mit einer index-Aktion:
cd gantt-app
rails generate controller gantt index
Sie sollten eine Bestätigung sehen, dass neue Dateien erstellt wurden.
Eine Standardroute festlegen
Um das Routing einzurichten, öffnen Sie config/routes.rb und ändern Sie die Standardroute, sodass sie auf die "index"-Aktion des neuen Controllers zeigt:
config/routes.rb
Rails.application.routes.draw do
root :to => "gantt#index"
end
Testen Sie nun Ihren Server mit:
rails server
Öffnen Sie anschließend http://localhost:3000/ in Ihrem Browser. Sie sollten eine leere Seite wie diese sehen:

Mit laufender App und vorbereiteter Standardseite können Sie nun das Gantt-Diagramm hinzufügen.
Gantt in die View einfügen
Jetzt können Sie das Gantt-Diagramm auf der Seite einbetten.
Öffnen Sie die Layout-Datei und fügen Sie ein yield innerhalb des head-Tags ein. Dadurch können dhtmlxGantt-Dateien eingebunden werden:
app/views/layouts/application.html.erb
<!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>
Öffnen Sie nun die gantt/index View und fügen Sie das Gantt-Diagramm hinzu:
app/views/gantt/index.html.erb
( 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>
Hier werden die dhtmlxGantt-Dateien vom CDN geladen, anstatt lokal eingebunden zu werden. Für Entwicklungszwecke können Sie auch die lesbaren Quellcodedateien aus dem Download-Paket verwenden.
Öffnen Sie erneut http://localhost:3000/ in Ihrem Browser. Sie sollten nun Folgendes sehen:

Sie haben jetzt ein Gantt-Diagramm, in dem Aufgaben hinzugefügt und bearbeitet werden können. Die Speicherfunktionalität fehlt jedoch noch. Diese wird im nächsten Schritt durch die Erstellung von Modellen hinzugefügt.
Schritt 3. Modelle erstellen
Da MySQL verwendet wird, stellen Sie sicher, dass Ihre Verbindungseinstellungen in config/database.yml korrekt sind, zum Beispiel:
config/database.yml
development:
adapter: mysql2
encoding: utf8
host: localhost
database: gantt-app
username: root
password:
Nun müssen Modelle für tasks und links erstellt werden.
Um das Task-Modell mit den entsprechenden Eigenschaften zu erstellen, führen Sie folgenden Befehl aus:
rails generate model Task
text:string
start_date:datetime
duration:integer
parent:integer
progress:decimal
Erstellen Sie auf ähnliche Weise das Link-Modell mit folgendem Befehl:
rails generate model Link
source:integer
target:integer
link_type:string:limit1
Beachten Sie, dass das dhtmlxGantt-Link-Objekt eine Eigenschaft namens type benötigt, um den Beziehungstyp (Start-zu-Start, Ende-zu-Ende, usw.) anzugeben.
Da der Name "type" in ActiveRecord reserviert ist, wird diese Eigenschaft hier als link_type benannt und die nötige Zuordnung später im Controller vorgenommen.
Eine vollständige Liste der Pflicht- und optionalen Eigenschaften finden Sie in der Dokumentation zum Task-Objekt und Link-Objekt.
Führen Sie danach die Migration aus, um die Datenbank zu aktualisieren:
rake db:migrate
Fügen Sie nun einige Testdaten hinzu:
- Öffnen Sie die Rails-Konsole:
rails c
- Fügen Sie ein paar Aufgaben und Links 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";
- Geben Sie "exit" ein, um die Konsole zu verlassen.
Im nächsten Schritt wird das Laden und Speichern von Daten im Controller implementiert.
Schritt 4. Daten laden
Mit den Modellen und der Migration können die Daten aus der Datenbank in das Gantt-Diagramm geladen werden.
Da dhtmlxGantt die Daten im JSON-Format erwartet, fügen Sie dem GanttController eine neue Aktion hinzu, die die Daten liest, formatiert und ausgibt:
app/controllers/gantt_controller.rb
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ügen Sie für diese Aktion eine Route in routes.rb hinzu:
config/routes.rb
Rails.application.routes.draw do
root :to => "gantt#index"
scope '/api' do/*!*/
get "/data", :to => "gantt#data"/*!*/
end/*!*/
end
Auf der Client-Seite rufen Sie diese Aktion mit der Methode gantt.load auf:
app/views/gantt/index.html.erb
gantt.config.date_format = "%Y-%m-%d %H:%i:%s";/*!*/
gantt.init("gantt_here");
gantt.load("/api/data");/*!*/
Die Konfiguration date_format legt das Datumsformat fest (z. B. das start_date einer Aufgabe), das vom Server empfangen wird und mit dem Datumsformat von Rails übereinstimmt.
Wenn Sie den Server starten und http://localhost:3000/ öffnen, sollte das Gantt-Diagramm nun mit Aufgaben und Verknüpfungen aus der Datenbank gefüllt sein. Änderungen werden jedoch noch nicht gespeichert - das wird im nächsten Schritt behandelt.
Schritt 5. Änderungen speichern
dhtmlxGantt kann alle Benutzeränderungen an ein RESTful API im Backend senden, wo sie in der Datenbank gespeichert werden können. Details zu diesem Protokoll finden Sie hier.
Um das Speichern zu ermöglichen, aktivieren Sie zunächst das Senden von Änderungen auf der Client-Seite:
app/views/gantt/index.html.erb
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");/*!*/
Als Nächstes werden zwei Controller hinzugefügt: einer für Tasks und einer für Links, jeweils mit den notwendigen Aktionen.
Task-Controller erstellen
Erzeugen Sie zunächst den Controller für Tasks:
rails generate controller task --no-helper --no-assets --no-view-specs
Da dieser Controller keine Views benötigt, verhindern die --no- Optionen das Anlegen unnötiger Dateien.
Implementieren Sie die Aktionen zum Erstellen, Aktualisieren und Löschen wie folgt:
app/controllers/task_controller.rb
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
Einige Hinweise zu diesem Code:
- Die get-Aktion wird hier nicht benötigt, da alle Daten bereits über gantt#data geladen werden.
- Die Eigenschaft progress ist auf dem Client standardmäßig möglicherweise nicht initialisiert, daher wird hier ein Standardwert vergeben. Alternativ könnte dieser Standardwert auch in der Modellklasse festgelegt werden (z. B. über eine Migration).
- Beim Erstellen eines neuen Elements gibt die Aktion die Datenbank-ID des neu eingefügten Datensatzes an den Client zurück.
Fügen Sie abschließend Routen für diese Aktionen hinzu, damit Benutzer Aufgaben im Gantt-Diagramm anzeigen, erstellen, aktualisieren und löschen können:
config/routes.rb
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
Im nächsten Schritt wird eine ähnliche Funktionalität für Links eingerichtet.
Erstellen des Link-Controllers
Erstellen Sie einen Link-Controller mit folgendem Befehl:
rails generate controller link --no-helper --no-assets --no-view-specs
Hier ist ein Beispiel, wie die Implementierung aussehen könnte:
app/controllers/link_controller.rb
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
Fügen Sie als Nächstes Routen für die neuen Aktionen hinzu:
config/routes.rb
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
Das ist alles, was notwendig ist. Sobald die Anwendung läuft, verfügt sie über ein interaktives Gantt-Diagramm, das von Rails und MySQL unterstützt wird:

Weitere Funktionen von dhtmlxGantt finden Sie in unseren Anleitungen.
Speichern der Aufgabenreihenfolge
Das Gantt-Diagramm auf der Client-Seite unterstützt das Umsortieren von Aufgaben per Drag-and-drop. Wenn Sie diese Funktion nutzen, muss die Reihenfolge der Aufgaben in der Datenbank gespeichert werden. Eine allgemeine Übersicht finden Sie hier.
Fügen wir diese Funktionalität der App hinzu.
Aufgaben-Umsortierung im Client aktivieren
Aktivieren Sie zunächst das Umsortieren der Aufgaben in der Benutzeroberfläche, indem Sie die Gantt-Konfiguration in der Index-Ansicht aktualisieren:
app/views/gantt/index.html.erb
gantt.config.order_branch = true;/*!*/
gantt.config.order_branch_free = true;/*!*/
gantt.init("gantt_here");
Aktualisieren Sie nun das Backend, um diese Änderungen widerzuspiegeln. Wir müssen dem Modell ein Sortierfeld hinzufügen, das wir sortorder nennen. Die aktualisierte Modelldeklaration könnte folgendermaßen aussehen:
rails generate model Task
text:string
start_date:datetime
duration:integer
parent:integer
progress:decimal
sortorder:integer /*!*/
Alternativ können Sie diese neue Eigenschaft zu einem bestehenden Modell hinzufügen:
- Erstellen Sie eine Migration:
rails generate migration add_sortorder_to_tasks sortorder:integer
- Bearbeiten Sie die generierte Migration, um einen Standardwert für die "sortorder"-Spalte festzulegen:
class AddSortorderToTasks < ActiveRecord::Migration[5.1]
def change
add_column :tasks, :sortorder, :integer, :default=>0
end
end
Führen Sie dann die Migration aus:
rake db:migrate
Aktualisieren Sie als Nächstes die CRUD-Operationen in den Controllern:
- Die data-Aktion sollte Aufgaben nach der
sortorder-Spalte sortiert zurückgeben:
app/controllers/gantt_controller.rb
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
- Beim Hinzufügen neuer Aufgaben wird ein initialer
sortorder-Wert vergeben:
app/controllers/task_controller.rb
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
- Wenn Aufgaben vom Benutzer umsortiert werden, passen Sie deren Reihenfolge entsprechend an:
app/controllers/task_controller.rb
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
Hier ist die Implementierung von Task.updateOrder:
app/models/task.rb
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
Applikationssicherheit
Gantt selbst bietet keinen Schutz gegen gängige Bedrohungen wie SQL-Injection, XSS oder CSRF-Angriffe. Es ist wichtig, dass Entwickler selbst für die Absicherung ihrer Backend-Implementierungen sorgen. Weitere Details finden Sie in diesem Artikel.
Fehlerbehebung
Wenn Sie die Schritte zur Integration von Gantt mit Ruby on Rails befolgt haben, aber Aufgaben und Links nicht auf der Seite angezeigt werden, sehen Sie sich den Leitfaden zur Fehlerbehebung unter Troubleshooting Backend Integration Issues an. Dort finden Sie Tipps zur Diagnose häufiger Probleme.
Wie geht es weiter?
Mit Ihrem nun voll funktionsfähigen Gantt-Diagramm können Sie den vollständigen Code auf GitHub einsehen. Dort steht er zum Klonen oder Herunterladen für eigene Projekte bereit.
Entdecken Sie außerdem Anleitungen zu verschiedenen Gantt-Funktionen oder Tutorials zur Integration von Gantt mit anderen Backend-Frameworks.