dhtmlxGantt bietet zwei Möglichkeiten zur Bearbeitung von Inhalten:
Mit der Inline-Bearbeitung können Änderungen direkt im Grid vorgenommen werden: Aufgaben erstellen und aktualisieren, Verknüpfungen zwischen ihnen setzen, Start- und Enddaten definieren oder die Dauer anpassen – alles über integrierte Editoren.
Um die Inline-Bearbeitung zu aktivieren, müssen Sie:
var textEditor = {type: "text", map_to: "text"};
var dateEditor = {type: "date", map_to: "start_date", min: new Date(2018, 0, 1),
max: new Date(2019, 0, 1)};
var durationEditor = {type: "number", map_to: "duration", min:0, max: 100};
gantt.config.columns = [
{name: "text", tree: true, width: '*', resize: true, editor: textEditor},
{name: "start_date", align: "center", resize: true, editor: dateEditor},
{name: "duration", align: "center", editor: durationEditor},
{name: "add", width: 44}
];
Related sample: Inline editing
Details zur inlineEditors Objekt-API finden Sie im Artikel Inline Editors Erweiterung.
Es gibt auch eine Videoanleitung, die zeigt, wie die Inline-Bearbeitung im Grid implementiert wird.
Die Inline-Editoren werden im Konfigurationsobjekt editor_types definiert.
Es sind mehrere Inline-Editoren vordefiniert:
{ type: "duration", map_to: "duration", formatter: formatter }
Dieser Editor-Typ ist hilfreich, wenn Dauern angegeben werden, die sowohl eine Zahl als auch eine Dauereinheit enthalten, z. B.: 5 days
. Standardmäßig wird der Duration Formatter verwendet. Sie können die Konfiguration auch anpassen oder einen benutzerdefinierten Formatter bereitstellen.
var editors = {
text: {type: "text", map_to: "text"},
start_date: {type: "date", map_to: "start_date", min: new Date(2018, 0, 1),
max: new Date(2019, 0, 1)},
end_date: {type: "date", map_to: "end_date", min: new Date(2018, 0, 1),
max: new Date(2019, 0, 1)},
duration: {type: "number", map_to: "duration", min:0, max: 100},
priority: {type:"select", map_to:"priority", options:gantt.serverList("priority")},
predecessors: {type: "predecessor", map_to: "auto"}
};
Seit v6.3 gibt es keine voreingestellten Minimal- oder Maximalwerte für date Inline-Editoren.
Wenn Sie möchten, dass die sichtbaren Daten auf der Zeitskala die min- und max-Werte des date Inline-Editors bestimmen (sofern keine eigenen min/max-Werte gesetzt sind), können Sie dynamische min/max Funktionen angeben:
const dateEditor = {type: "date", map_to: "start_date",
min: function(taskId){
return gantt.getState().min_date
},
max: function( taskId ){
return gantt.getState().max_date
}
};
Wenn Sie das Format für inklusive Enddaten für Aufgaben verwenden und die Inline-Bearbeitung im Grid korrekt unterstützen möchten, benötigen Sie einen speziellen Editor für inklusive Enddaten, z. B.:
// inklusiver Editor für Enddaten
// Verwenden Sie den Standardeditor, aber überschreiben Sie die set_value/get_value-Methoden
var dateEditor = gantt.config.editor_types.date;
gantt.config.editor_types.end_date = gantt.mixin({
set_value: function(value, id, column, node){
var correctedValue = gantt.date.add(value, -1, "day");
return dateEditor.set_value.apply(this, [correctedValue, id, column, node]);
},
get_value: function(id, column, node) {
var selectedValue = dateEditor.get_value.apply(this, [id, column, node]);
return gantt.date.add(selectedValue, 1, "day");
},
}, dateEditor);
var textEditor = {type: "text", map_to: "text"};
var startDateEditor = {type: "date", map_to: "start_date"};
var endDateEditor = {type: "end_date", map_to: "end_date"};
var durationEditor = {type: "number", map_to: "duration", min:0, max: 100};
gantt.config.columns = [
{name: "text", label: "Name", tree: true, width: 200, editor: textEditor,
resize: true},
{name: "duration", label: "Duration", width:80, align: "center",
editor: durationEditor, resize: true},
{name: "start_date", label: "Start", width:140, align: "center",
editor: startDateEditor, resize: true},
{name: "end_date", label: "Finish", width:140, align: "center",
editor: endDateEditor, resize: true}
];
// Lightbox und Grid-Templates aktualisieren, um inklusive Enddaten anzuzeigen
gantt.templates.task_end_date = function(date){
return gantt.templates.task_date(new Date(date.valueOf() - 1));
};
var gridDateToStr = gantt.date.date_to_str("%Y-%m-%d");
gantt.templates.grid_date_format = function(date, column){
if(column === "end_date"){
return gridDateToStr(new Date(date.valueOf() - 1));
}else{
return gridDateToStr(date);
}
}
Related sample: Inclusive end date editor
Weitere Informationen zur Formatierung von Enddaten finden Sie im Artikel Task end date display & Inclusive end dates.
Diese Funktion ist nur in der PRO Edition verfügbar.
Ab Version 6.3 unterstützt Gantt die Angabe von Link-Typen sowie Zeitabständen (Lag/Lead) direkt im Inline-Editor.
Um dies zu ermöglichen, verwenden Sie das Link Formatter Modul und übergeben eine Instanz von LinksFormatter an den predecessor Editor:
var formatter = gantt.ext.formatters.durationFormatter({
enter: "day",
store: "day",
format: "auto"
});
var linksFormatter = gantt.ext.formatters.linkFormatter({durationFormatter: formatter});
var editors = {
text: {type: "text", map_to: "text"},
start_date: {type: "date", map_to: "start_date",
min: new Date(2018, 0, 1), max: new Date(2019, 0, 1)},
end_date: {type: "date", map_to: "end_date",
min: new Date(2018, 0, 1), max: new Date(2019, 0, 1)},
duration: {type: "duration", map_to: "duration",
min:0, max: 100, formatter: formatter},
priority: {type: "select", map_to: "priority",
options:gantt.serverList("priority")},
predecessors: {type: "predecessor", map_to: "auto", formatter: linksFormatter} };
gantt.config.columns = [
{name: "wbs", label: "#", width: 60, align: "center", template: gantt.getWBSCode},
{name: "text", label: "Name", tree: true, width: 200, editor: editors.text,
resize: true},
{name: "start_date", label: "Start", width:80, align: "center",
editor: editors.start_date, resize: true},
{name: "predecessors", label: "Predecessors",width:80, align: "left",
editor: editors.predecessors, resize: true, template: function(task){
var links = task.$target;
var labels = [];
for(var i = 0; i < links.length; i++){
var link = gantt.getLink(links[i]);
labels.push(linksFormatter.format(link)); }
return labels.join(", ")
}},
{name:"add"}
];
Related sample: Inline editing - keyboard navigation mode
Nachfolgend finden Sie Codebeispiele für benutzerdefinierte Editoren wie:
Es ist möglich, einen eigenen Inline-Editor zu erstellen, indem Sie ein neues Editor-Objekt wie folgt definieren:
gantt.config.editor_types.custom_editor = {
show: function (id, column, config, placeholder) {
// Wird aufgerufen, wenn das Eingabefeld angezeigt wird. HTML-Markup in placeholder einfügen
// und ggf. Editoren initialisieren:
var html = "<div><input type='text' name='" + column.name + "'></div>";
placeholder.innerHTML = html;
},
hide: function () {
// Wird aufgerufen, wenn das Eingabefeld ausgeblendet wird.
// Hier können komplexe Editoren bereinigt oder Event-Listener entfernt werden
},
set_value: function (value, id, column, node) {
// Wert in das Eingabefeld setzen
},
get_value: function (id, column, node) {
// Wert aus dem Eingabefeld zurückgeben
},
is_changed: function (value, id, column, node) {
// Wird vor dem Speichern/Schließen aufgerufen. true zurückgeben, wenn sich der neue Wert geändert hat
// true löst das Speichern aus, false überspringt das Speichern
},
is_valid: function (value, id, column, node) {
// Eingabe validieren, false verwirft Änderungen
return true/false;
},
save: function (id, column, node) {
// Für Eingaben mit map_to:auto. Komplexe Speicherlogik wird hier implementiert
},
focus: function (node) {
}
}
Hier eine Übersicht der Methoden:
map_to:auto
. Parameter:
Wichtige Hinweise für wiederverwendbare Editoren:
get_value
nur den aktuellen Wert des Editors zurückgeben, ohne das Aufgabenobjekt zu verändern. Wenn gültig, aktualisiert Gantt die Aufgabe automatisch.map_to
-Option, um anzugeben, welches Aufgabenattribut der Editor aktualisiert, um die Wiederverwendbarkeit sicherzustellen.hide
-Methode kann meist leer bleiben, es sei denn, Sie müssen Event-Handler entfernen oder komplexe Widgets zerstören.is_changed
und is_valid
:
is_changed
sollte nur true zurückgeben, wenn sich der Wert tatsächlich geändert hat, um unnötige Updates zu vermeiden.is_valid
verhindert ungültige Eingaben.save
und setzen map_to
auf "auto". In diesem Fall ändert Gantt das Aufgabenobjekt nicht direkt, sondern ruft save
auf, um Änderungen anzuwenden.Hier ein Beispiel für einen einfachen numerischen Eingabeeditor. Die hide
-Methode bleibt leer, und save
wird nicht implementiert.
var getInput = function(node){
return node.querySelector("input");
};
gantt.config.editor_types.simpleNumber = {
show: function (id, column, config, placeholder) {
var min = config.min || 0,
max = config.max || 100;
var html = "<div><input type='number' min='" + min +
"' max='" + max + "' name='" + column.name + "'></div>";
placeholder.innerHTML = html;
},
hide: function () {
// leer, da nach Entfernen des Editors keine Bereinigung notwendig ist
},
set_value: function (value, id, column, node) {
getInput(node).value = value;
},
get_value: function (id, column, node) {
return getInput(node).value || 0;
},
is_changed: function (value, id, column, node) {
var currentValue = this.get_value(id, column, node);
return Number(value) !== Number(currentValue);
},
is_valid: function (value, id, column, node) {
return !isNaN(parseInt(value, 10));
},
focus: function (node) {
var input = getInput(node);
if (!input) {
return;
}
if (input.focus) {
input.focus();
}
if (input.select) {
input.select();
}
}
};
Danach können Sie ihn wie einen integrierten Editor verwenden:
var numberEditor = {type: "simpleNumber", map_to: "quantity", min:0, max: 50};
gantt.config.columns = [
...
{name: "quantity", label: "Quantity", width: 80, editor: numberEditor,
resize: true},
...
];
Da Gantt das Editor-DOM-Element automatisch entfernt, ist in hide
keine zusätzliche Bereinigung notwendig.
Wenn Sie komplexere Widgets in Inline-Editoren verwenden, ist es sinnvoll, eine hide
-Methode zur Bereinigung hinzuzufügen.
Hier ein Beispiel für eine DatePicker-Eingabe mit jQuery UI. Das Datepicker-Widget muss zerstört werden, wenn der Editor aus dem DOM entfernt wird.
Voraussetzungen:
<link rel="stylesheet" href="//code.jquery.com/ui/1.12.1/themes/base/jquery-ui.css">
<script src="https://code.jquery.com/jquery-1.12.4.js"></script>
<script src="https://code.jquery.com/ui/1.12.1/jquery-ui.js"></script>
Editor:
gantt.config.editor_types.custom_datepicker_editor = {
show: function (id, column, config, placeholder) {
placeholder.innerHTML = "<div><input type='text' id='datepicker' name='" +
column.name + "'></div>";
$("#datepicker").datepicker({
dateFormat: "yy-mm-dd",
onSelect: function(dateStr){
gantt.ext.inlineEditors.save()
}
});
},
hide: function (node) {
$("#datepicker").datepicker( "destroy" );
},
set_value: function (value, id, column, node) {
$("#datepicker").datepicker("setDate", value);
},
get_value: function (id, column, node) {
return $("#datepicker").datepicker( "getDate" );
},
is_changed: function (value, id, column, node) {
return (+$("#datepicker").datepicker( "getDate" ) !== +value);
},
is_valid: function (value, id, column, node) {
return !(isNaN(+$("#datepicker").datepicker( "getDate" )))
},
save: function (id, column, node) {
},
focus: function (node) {
}
};
let dateEditor = {
type: "custom_datepicker_editor",
map_to: "start_date"
};
gantt.config.columns = [
{name: "text", tree: true, width: '*', resize: true},
{name: "start_date", align: "center", resize: true, editor: dateEditor},
{name: "duration", align: "center"},
{name: "add", width: 44}
];
Related sample: Using jQuery Datepicker in the editor
Die save
-Funktion ist nützlich, wenn der Editor mehrere Eigenschaften einer Aufgabe gleichzeitig aktualisieren muss oder wenn er Objekte außer Aufgaben ändern soll.
In solchen Fällen können Sie weiterhin get_value
für die eingebaute Validierung implementieren, aber Gantt versucht nicht, den Wert des Editors direkt auf die Aufgabe anzuwenden. Stattdessen wird die save
-Funktion aufgerufen.
Sobald save
aufgerufen wird, sollten Sie die Eingabewerte interpretieren und die erforderlichen Änderungen am Gantt-Diagramm mit Ihrem eigenen Code vornehmen. Nachdem die save
-Methode abgeschlossen ist, löst Gantt das onSave-Event aus. Es wird jedoch kein gantt.updateTask für die aktualisierte Zeile aufgerufen.
Hinweis! Die save
-Methode wird nur aufgerufen, wenn Sie map_to:"auto"
in der Editor-Konfiguration setzen:
var editors = {
...
predecessors: {type: "predecessor", map_to: "auto"}
};
Ein gutes Beispiel hierfür ist der eingebaute Vorgänger-Editor. Eine vereinfachte Implementierung finden Sie im zugehörigen Beispiel:
Related sample: Built-in predecessor editor
Dieser Modus geht davon aus, dass Sie mit der Maus Zellen fokussieren und mit Tastenkombinationen zwischen ihnen navigieren:
Related sample: Inline editing
Hier wird die Tastatur sowohl zur Navigation als auch zur Bearbeitung von Rasterzellen mit vordefinierten Tasten oder Tastenkombinationen verwendet:
Um die Tastaturnavigation für die Bearbeitung zu aktivieren, müssen Sie:
gantt.plugins({
keyboard_navigation: true
});
gantt.config.keyboard_navigation = true;
gantt.config.keyboard_navigation_cells = true;
Sie können auch eine Platzhalterzeile aktivieren, die als leere Zeile am Ende der Aufgabenliste angezeigt wird. Benutzer können diese Zeile bearbeiten, um neue Aufgaben hinzuzufügen.
gantt.config.placeholder_task = true;
Alternativ kann der Fokus direkt nach dem Hinzufügen einer neuen Aufgabe auf die Platzhalteraufgabe verschoben werden:
gantt.config.placeholder_task = {
focusOnCreate: true
};
Falls erforderlich, können Sie auch die automatische Erkennung von Aufgabentypen aktivieren:
gantt.config.auto_types = true;
Related sample: Inline editing - keyboard navigation mode
Es ist möglich, eigene Tastaturzuweisungen zu definieren – Sie können festlegen, wie Editoren geöffnet werden und Editor-Ereignisse wie Öffnen, Schließen, Bearbeitungsbeginn und -ende behandeln. Dazu erstellen Sie ein separates Objekt mit dieser Logik und übergeben es an eine spezielle Methode, um Ihre Belegung anzuwenden:
var mapping = {
init: function(inlineEditors){
// inlineEditor-Modul initialisiert
// globale Listener für Bearbeitungsbeginn/-ende hinzufügen
},
onShow: function(inlineEditors, node){
// Editor wird angezeigt
},
onHide: function(inlineEditors, node){
// Editor wird ausgeblendet
// Änderungen aus onShow ggf. bereinigen
}
};
gantt.ext.inlineEditors.setMapping(mapping);
Related sample: Inline editing - Custom keyboard mapping
Stellen Sie sich vor, Sie verwenden Tastaturnavigation, Inline-Editoren und eine Platzhalteraufgabe in Ihrem Gantt-Diagramm. Zwei typische Szenarien:
Szenario 1. Nach Eingabe eines Namens für eine neue Platzhalteraufgabe und Drücken von Tab erwarten Sie, dass Gantt die nächste Zelle dieser Aufgabe öffnet. Stattdessen wird der Fokus auf die nächste Platzhalteraufgabe darunter verschoben und der Inline-Editor nicht geöffnet.
Szenario 2. Nach Eingabe eines Namens für eine neue Platzhalteraufgabe und Klicken auf die nächste Zelle verschiebt Gantt den Fokus auf die nächste Platzhalteraufgabe statt auf die Zelle, auf die Sie geklickt haben.
Mit einer benutzerdefinierten Zuordnung können Sie festlegen, wie der Inline-Editor auf Maus- und Tastaturaktionen reagieren soll. Hier ein Beispiel:
Related sample: Gantt. Custom mapping for placeholder task
Beim Bearbeiten von Zellen im Grid können Fehler auftreten.
Um das Speichern ungültiger Werte zu verhindern, sollten Sie die Eingaben vor dem Schließen des Editors validieren. Dies kann auf zwei Arten erfolgen:
So wirkt sich die Validierung auf das Editorverhalten aus:
Wenn Sie einen Editor in einer Grid-Zelle mit der Maus öffnen, gilt Folgendes:
Weitere Informationen zu clientseitiger oder serverseitiger Validierung finden Sie im Artikel Validierung.
Wenn die Validierung aktiviert ist, setzt Gantt ungültige Eingaben zurück und schließt den Editor. Sie müssen die Zelle erneut öffnen, um Werte zu korrigieren.
Eine praktische Möglichkeit, dies zu vermeiden, ist das Anzeigen eines Hinweises, der den Benutzer zur Korrektur auffordert. Dies lässt sich mit einer benutzerdefinierten Tastaturbelegung umsetzen, zum Beispiel:
function editAnotherCell(inlineEditors){
var value = inlineEditors.getValue();
if(confirm(`does '${value}' look ok to you?`)){
inlineEditors.save();
}
}
var mapping = {
init: function(inlineEditors){
gantt.attachEvent("onTaskClick", function (id, e) {
var cell = inlineEditors.locateCell(e.target);
if (cell && inlineEditors.getEditorConfig(cell.columnName)) {
if (inlineEditors.isVisible()) editAnotherCell(inlineEditors)
else inlineEditors.startEdit(cell.id, cell.columnName);
return false;
}
return true;
});
gantt.attachEvent("onEmptyClick", function () {
inlineEditors.hide();
return true;
});
},
onShow: function(inlineEditors, node){
node.onkeydown = function (e) {
e = e || window.event;
if(e.defaultPrevented){
return;
}
var keyboard = gantt.constants.KEY_CODES;
var shouldPrevent = true;
switch (e.keyCode) {
case gantt.keys.edit_save:
var value = inlineEditors.getValue();
if(confirm(`does '${value}' look ok to you?`)){
inlineEditors.save();
}
break;
case gantt.keys.edit_cancel:
inlineEditors.hide();
break;
case keyboard.TAB:
if(e.shiftKey){
if (inlineEditors.isVisible()) editAnotherCell(inlineEditors)
else inlineEditors.editPrevCell(true);
}else{
if (inlineEditors.isVisible()) editAnotherCell(inlineEditors)
else inlineEditors.editNextCell(true);
}
break;
default:
shouldPrevent = false;
break;
}
if(shouldPrevent){
e.preventDefault();
}
};
},
onHide: function(inlineEditors, node){}
};
gantt.ext.inlineEditors.setMapping(mapping);
gantt.init("gantt_here");
Related sample: Custom keyboard mapping
Im Einzel-Auswahlmodus öffnet ein Klick auf eine Aufgabe sofort den Inline-Editor.
Im Multi-Auswahlmodus wird beim Klicken auf eine nicht ausgewählte Aufgabe diese ausgewählt, aber der Inline-Editor öffnet sich erst nach einem zweiten Klick. Soll der Editor bereits beim ersten Klick geöffnet werden, aktivieren Sie die Konfiguration inline_editors_multiselect_open:
gantt.plugins({
multiselect: true
});
...
gantt.config.inline_editors_multiselect_open = true;
Zurück nach oben