Библиотека dhtmlxGantt предлагает два основных способа редактирования содержимого:
Встроенное редактирование позволяет вносить изменения прямо в гриде. Вы можете создавать и обновлять задачи, устанавливать связи, задавать даты начала и окончания, а также изменять продолжительность с помощью встроенных редакторов.
Чтобы активировать встроенное редактирование, вам нужно:
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
Более подробная информация об API объекта inlineEditors доступна в статье Расширение Inline Editors.
Для практической демонстрации, посмотрите видео руководство по внедрению встроенного редактирования в гриде.
Встроенные редакторы определяются в объекте конфигурации editor_types
. Библиотека предоставляет несколько предварительно настроенных редакторов:
{ type: "duration", map_to: "duration", formatter: formatter }
Этот редактор удобен, когда нужно указать продолжительность, включающую как числовое значение, так и единицу измерения продолжительности, например, 5 дней
. По умолчанию используется Duration 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: "number", map_to: "duration", min:0, max: 100},
priority: {type:"select", map_to:"priority", options:gantt.serverList("priority")},
predecessors: {type: "predecessor", map_to: "auto"}
};
Начиная с версии 6.3, нет ограничений по умолчанию для минимальных и максимальных значений ввода в date встроенных редакторах. Если вы хотите, чтобы видимые даты на временной шкале ограничивали диапазон ввода, вы можете установить динамические значения min/max:
const dateEditor = {type: "date", map_to: "start_date",
min: function(taskId){
return gantt.getState().min_date
},
max: function(taskId){
return gantt.getState().max_date
}
};
Для задач, использующих инклюзивный формат конечной даты, вы можете создать специфический редактор для корректной работы с этим форматом:
// инклюзивный редактор для конечных дат
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}
];
// Настройка шаблонов для отображения дат в инклюзивном формате
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: Инклюзивный редактор конечной даты
Для получения более подробной информации, ознакомьтесь со статьей Отображение конечной даты задачи и инклюзивные конечные даты.
Эта функция доступна только в PRO-версии.
Начиная с версии 6.3, Gantt поддерживает указание типов связей и значений задержки/опережения непосредственно из встроенного редактора. Чтобы включить эту возможность, используйте модуль Link Formatter и передайте экземпляр LinksFormatter редактору predecessor:
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
Если нужно, вы можете создать пользовательский встроенный редактор. Начните с определения нового объекта редактора:
gantt.config.editor_types.custom_editor = {
show: function (id, column, config, placeholder) {
var html = "<div><input type='text' name='" + column.name + "'></div>";
placeholder.innerHTML = html;
},
hide: function () {},
set_value: function (value, id, column, node) {},
get_value: function (id, column, node) {},
is_changed: function (value, id, column, node) {},
is_valid: function (value, id, column, node) { return true; },
save: function (id, column, node) {},
focus: function (node) {}
}
Эта структура позволяет определить, как редактор ведет себя при отображении, скрытии или при установке, проверке или сохранении значений.
Функция save
полезна, когда вам нужно обновить несколько свойств задачи одновременно или когда вы хотите изменить объекты, отличные от задач. В таких случаях вы все равно можете использовать функцию get_value
для встроенной проверки, но сам Gantt не будет применять значение редактора к задаче. Вместо этого он вызовет функцию save
.
После вызова save
вам нужно будет обработать входные значения и применить необходимые изменения к диаграмме Gantt с помощью пользовательского кода. После завершения функции save
будет вызвано событие onSave. Однако Gantt не вызовет автоматически gantt.updateTask для обновленной строки.
Важно: Функция save
будет выполнена только в том случае, если вы установили map_to:"auto"
в конфигурации редактора:
var editors = {
...
predecessors: {type: "predecessor", map_to: "auto"}
};
Хороший пример этого - встроенный редактор предшественников. Упрощенная версия его реализации представлена в примере ниже:
Related sample: Встроенный редактор предшественников
Этот режим позволяет редактировать ячейки, щелкая по ним мышью, и перемещаться по ячейкам с помощью горячих клавиш:
Related sample: Inline editing
В этом режиме вы можете использовать клавиатуру как для навигации, так и для редактирования ячеек грида. Предопределенные клавиши или комбинации клавиш помогают в процессе:
Чтобы включить навигацию с помощью клавиатуры для редактирования, выполните следующие шаги:
gantt.plugins({
keyboard_navigation: true
});
gantt.config.keyboard_navigation = true;
gantt.config.keyboard_navigation_cells = true;
Вы также можете активировать строку-заполнитель, которая представляет собой пустую строку в конце списка задач для добавления новых задач:
gantt.config.placeholder_task = true;
Чтобы автоматически фокусироваться на задаче-заполнителе после добавления новой, используйте эту конфигурацию:
gantt.config.placeholder_task = {
focusOnCreate: true
};
Кроме того, вы можете включить автоматическое определение типа задачи:
gantt.config.auto_types = true;
Related sample: Inline editing - keyboard navigation mode
Вы можете настроить сопоставление клавиш, чтобы определить, как открываются редакторы, обрабатываются события, связанные с редактором (например, открытие и закрытие), и управляется логика редактирования. Это делается путем создания пользовательского объекта конфигурации и передачи его в конкретный метод:
var mapping = {
init: function(inlineEditors){
// Инициализация модуля InlineEditor
// Добавление глобальных слушателей для событий редактирования
},
onShow: function(inlineEditors, node){
// Действия, которые выполняются при отображении редактора
},
onHide: function(inlineEditors, node){
// Действия по очистке при скрытии редактора
}
};
gantt.ext.inlineEditors.setMapping(mapping);
Related sample: Inline editing - Custom keyboard mapping
Представьте два сценария при использовании навигации с клавиатуры, встроенных редакторов и задачи-заполнителя:
Сценарий 1: После ввода имени для новой задачи-заполнителя и нажатия Tab вы ожидаете, что следующая ячейка задачи откроется. Однако фокус перемещается на новую задачу-заполнитель ниже, не открывая редактор.
Сценарий 2: Если вы вводите имя для новой задачи-заполнителя и щелкаете на следующей ячейке, фокус перемещается на задачу-заполнитель вместо щелкнутой ячейки.
Вы можете решить эти проблемы, определив пользовательскую логику для обработки взаимодействий с мышью и клавиатурой с встроенным редактором. Вот пример:
Related sample: Gantt. Пользовательское сопоставление для задачи-заполнителя
При редактировании ячейки в гриде могут возникнуть ошибки. Чтобы предотвратить сохранение неверных значений, вы должны проверять ввод перед закрытием редактора. Это можно сделать двумя способами:
Вот как ведет себя редактор с включенной валидацией:
Для получения более подробной информации о валидации на стороне клиента или сервера, ознакомьтесь со статьей Валидация.
По умолчанию Gantt сбрасывает недопустимые входные значения и закрывает редактор, когда проверка не выполняется. Чтобы избежать повторного открытия редактора, вы можете использовать окно предупреждения, чтобы позволить пользователям исправить некорректные значения. Это требует пользовательского сопоставления клавиатуры, как показано ниже:
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: Пользовательское сопоставление клавиатуры
В режиме одиночного выбора клик по задаче открывает встроенный редактор.
В режиме множественного выбора клик по невыбранной задаче сначала выбирает её, и редактор открывается только на второй клик. Чтобы включить редактор при первом клике в режиме множественного выбора, используйте конфигурацию inline_editors_multiselect_open:
gantt.plugins({
multiselect: true
});
...
gantt.config.inline_editors_multiselect_open = true;
К началу