Загрузка данных

dhtmlxGantt поддерживает два формата данных для загрузки:

Для загрузки данных в диаграмму Ганта вы можете использовать методы gantt.parse или gantt.load.

gantt.init("gantt_here");
gantt.load("tasks.json");

Related sample:  Basic initialization

Если переданные в Gantt данные некорректны, их древовидная структура может стать циклической, что приведет к ошибке циклической ссылки.

Загрузка из объекта

Если вы хотите загрузить данные непосредственно из объекта, метод gantt.parse — это то, что вам нужно:

Загрузка из встроенного источника данных

var data = {
  tasks:[
     {id:1, text:"Project #1", start_date:"01-04-2020", duration:18},
     {id:2, text:"Task #1", start_date:"02-04-2020", duration:8, parent:1},
     {id:3, text:"Task #2", start_date:"11-04-2020", duration:8, parent:1}
   ]
};
gantt.init("gantt_here");
gantt.parse(data);

Related sample:  Basic initialization

Если ваши данные содержат значения "start_date" и "end_date", и даты представлены в упрощенном формате (например, 01-12-2021 без времени), может потребоваться дополнительная конфигурация. Ознакомьтесь с этим руководством: Отображение даты окончания задачи и включительные даты окончания.

Загрузка с сервера

На стороне клиента

Для получения данных с сервера используйте метод gantt.load:

gantt.html

gantt.init("gantt_here");
gantt.load("data.json");

Метод load отправляет AJAX-запрос на указанный URL и ожидает ответ в одном из поддерживаемых форматов. Например:

data.json

{
  "tasks":[
     {"id":1, "text":"Project #1", "start_date":"01-04-2020", "duration":18},
     {"id":2, "text":"Task #1", "start_date":"02-04-2020","duration":8, "parent":1},
     {"id":3, "text":"Task #2", "start_date":"11-04-2020","duration":8, "parent":1}
  ],
  "links":[
     {"id":1, "source":1, "target":2, "type":"1"},
     {"id":2, "source":2, "target":3, "type":"0"}
  ]
}

Вы можете указать формат данных во втором параметре, например, "json", "xml" или "oldxml":

gantt.load("data.xml", "xml");

На стороне сервера

На серверной стороне вы можете использовать либо статический файл, либо скрипт для сбора данных из базы данных или других источников и отправки их в ответ. Реализация зависит от используемого серверного фреймворка.

Подробные инструкции и примеры для различных платформ можно найти в статье Интеграция на стороне сервера.

Например, с Node.js вы можете определить маршрут сервера для обработки AJAX-запросов Gantt:

gantt.load("/data");

Этот маршрут должен возвращать ответ в формате JSON, например:

app.get("/data", function(req, res){
    db.query("SELECT * FROM gantt_tasks", function(err, rows){
        if (err) console.log(err);
        db.query("SELECT * FROM gantt_links", function(err, links){
            if (err) console.log(err);
            for (var i = 0; i < rows.length; i++){
                rows[i].start_date = rows[i].start_date.format("YYYY-MM-DD");
                rows[i].open = true;
            }
 
            res.send({ tasks:rows, links : links });
        });
    });
});

Список поддерживаемых форматов данных можно найти в статье Поддерживаемые Форматы Данных.

Загрузка дат задач

Установка расписания задачи

Существует три способа определить расписание задачи в потоке данных:

  • С использованием start_date и duration
  • С использованием start_date и end_date
  • С использованием duration и end_date

Если одно из свойств отсутствует, Gantt рассчитывает его на основе двух других.

Related sample:  Backward planning

Свойство end_date имеет приоритет над duration. Если указаны все три свойства, Gantt игнорирует duration и рассчитывает новое значение для него. Например:

{
    "id":"20", "text":"Project #2", 
    "start_date":"01-04-2025", 
    "duration":3, 
    "end_date":"05-04-2025", 
    "order":10,"progress":0.4, 
    "type": "project", "open": true
}
 
// Задача будет загружена с пересчитанной длительностью на основе 'start_date' и 'end_date'
{
    "id":"20", "text":"Project #2", 
    "start_date":"01-04-2025", 
    "duration":4, 
    "end_date":"05-04-2025", 
    "order":10,"progress":0.4, 
    "type": "project", "open": true
}

Загрузка дат в формате ISO

Чтобы использовать формат даты ISO, переопределите функции парсинга и форматирования дат в Gantt:

gantt.templates.parse_date = function(date) { 
    return new Date(date);
};
gantt.templates.format_date = function(date) { 
    return date.toISOString();
};

Динамическое изменение формата даты

Если вам нужно изменить формат даты на лету, обновите шаблон parse_date следующим образом:

var cfg = gantt.config;
var strToDate = gantt.date.str_to_date(cfg.date_format, cfg.server_utc);
 
gantt.templates.parse_date = function(date){
    return strToDate(date);
};

Отображение даты окончания задачи и включительные даты окончания

Вот некоторая ясность о том, как правильно обрабатывать и отображать даты окончания задач.

Сценарии для рассмотрения

Сценарий 1

  • Длительность задачи указана в полных днях (duration_unit="day").
  • Данные задачи содержат даты начала и окончания в форматах типа "%Y-%m-%d" или "%d-%m-%Y" (без времени).

Gantt может интерпретировать эти даты так, что это приведет к неожиданным результатам. Например:

gantt.parse({ tasks: [
    { 
        id: 1,
        text: "Task 1",
        start_date: "22-12-2021",
        end_date: "22-12-2021"
    }
]}, links:[]);
 
console.log(gantt.getTask(1).end_date);
// 22 декабря 2021 00:00:00
 
console.log(gantt.getTask(1).duration);
// 0

Здесь даты начала и окончания указывают на один и тот же момент времени, в результате чего длительность равна 0.

Сценарий 2

  • Дата окончания задачи видна в гриде.
  • Формат даты окончания не содержит временных деталей.
gantt.config.columns = [
    {name: "text", label: "Name", tree: true, width: 200, resize: true},
    {name: "duration", label: "Duration", width:80, align: "center", resize: true},
    {name: "start_date", label: "Start", width:80, align: "center", resize: true},
    {name: "end_date", label: "Finish", width:80, align: "center", resize: true}
];
 
gantt.init("gantt_here");
 
gantt.parse({ tasks: [
    { 
        id: 1,
        text: "Task 1",
        start_date: "02-04-2020",
        end_date: "02-04-2020"
    }
]}, links:[]);

В этом случае отображаемая дата окончания может показаться вводящей в заблуждение, так как она может выглядеть как 3 апреля, хотя задача фактически заканчивается 2 апреля.

Как Gantt сохраняет даты окончания?

Даже если даты задачи не содержат временных деталей, Gantt сохраняет их как объекты JS Date с включенным временем. Вот как это работает:

  • Секунды и миллисекунды всегда устанавливаются на 0.
  • Дата окончания представляет начало следующего дня или часа после последнего занятого момента задачи:
    • Для задачи, начинающейся 2 апреля и длительностью 1 день: "02-04-2022 00:00:00 - 03-04-2022 00:00:00".
    • Для задачи, начинающейся 2 апреля в 13:00 и длительностью 1 час: "02-04-2022 13:00:00 - 02-04-2022 14:00:00".

Если дата окончания отображается без времени, она может выглядеть вводящей в заблуждение, как показано в Сценарии 2. Однако это поведение по умолчанию можно изменить через конфигурацию. Следующий раздел предоставляет решения для этого.

Настройка поведения по умолчанию

1) Первое, чего стоит избегать, это изменение фактических дат задач, хранящихся в диаграмме Ганта.

Иногда вы можете рассмотреть возможность изменения дат задач, загружаемых в Gantt, например, установив даты окончания как "02-04-2022 23:59:59". Однако этот подход может помешать расчетам длительности задач и автоматическому планированию, поэтому не рекомендуется.

Вместо этого вот несколько лучших альтернатив:

2a) Если вы хотите изменить, как даты окончания задач отображаются в Gantt (например, включить дату окончания в длительность задачи), вы можете переопределить шаблон task_end_date.

Например, для задачи, начинающейся 2 апреля 2020 года и длительностью один день, по умолчанию дата окончания будет отображаться как 3 апреля 2020 года (03-04-2020 00:00:00):

Применяя шаблон task_end_date, дата окончания будет отображаться как 2 апреля 2020 года:

Вот код для этого изменения:

// переопределение конфигурации колонок
gantt.config.columns = [
  {name: "wbs", label: "#", width: 60, align: "center", template: gantt.getWBSCode},
  {name: "text", label: "Name", tree: true, width: 200, resize: true},
  {name: "start_date", label: "Start", width:80, align: "center", resize: true},
  {name: "end_date", label: "Finish", width:80, align: "center", resize: true}, 
  {name:"add"}
];
 
// переопределение шаблона
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); 
   }
}
gantt.init("gantt_here");

Этот подход обновляет дату окончания задачи во всем гриде, заголовках лайтбокса и других местах, где появляется дата окончания.

Если вы используете включительный формат даты окончания и хотите, чтобы он работал с встроенным редактированием, вам потребуется создать пользовательский редактор для редактирования включительных дат окончания задач. Вот пример:

// включительный редактор для дат окончания
// используйте редактор по умолчанию, но переопределите методы set_value/get_value
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:  Редактор включительных дат окончания

2b) Если ваше приложение требует, чтобы "включительные" даты окончания были сохранены (например, однодневная задача, начинающаяся 2 апреля 2020 года, будет иметь start_date: "02-04-2022" и end_date: "02-04-2022"), вам нужно будет выполнить следующее:

  • Добавьте один день к датам окончания перед загрузкой данных в Gantt.
  • Вычтите один день из дат окончания перед сохранением изменений обратно в хранилище.

Свойства данных

Источник данных диаграммы Ганта — это объект, содержащий два типа информации:

  • tasks - элементы задач.
  • links - связи зависимостей.

Свойства объекта задачи

Для полного списка свойств объекта задачи, смотрите статью Свойства задачи.

Формат даты по умолчанию для данных JSON и XML — "%d-%m-%Y %H:%i" (смотрите спецификацию формата даты).
Чтобы изменить это, используйте параметр конфигурации date_format.

gantt.config.date_format="%Y-%m-%d";
gantt.init("gantt_here");

После загрузки данных в Gantt, свойства start_date и end_date разбираются в тип Date.

Если вы используете форматы дат, не поддерживаемые date_format, вы можете вручную разобрать их с помощью шаблона parse_date.

Для полного списка свойств объекта связи, смотрите статью Свойства связи.

Пользовательские свойства

Вы можете добавлять пользовательские свойства к элементам данных, помимо обязательных. Эти дополнительные свойства будут разобраны как строки и загружены на клиентскую сторону для дальнейшего использования по мере необходимости.

Ознакомьтесь с примерами данных с пользовательскими свойствами здесь.


Структура базы данных

При использовании базы данных лучше иметь отдельные таблицы для задач и связей.

Рекомендуемая структура для базы данных для загрузки задач и связей в диаграмму Ганта выглядит следующим образом:

  • таблица gantt_tasks - для задач Gantt
    • id - (строка, число) ID задачи.
    • start_date - (Дата) дата начала задачи.
    • text - (строка) описание задачи.
    • progress - (число) процент завершения (от 0 до 1).
    • duration - (число) длительность задачи в единицах текущей временной шкалы.
    • parent - (число) ID родительской задачи.
    • type - (строка) необязательно, тип задачи.
    • readonly - (булево) необязательно, помечает задачу как только для чтения.
    • editable - (булево) необязательно, помечает задачу как редактируемую.
  • таблица gantt_links - для связей зависимостей Gantt
    • id - (строка, число) ID связи.
    • source - (число) ID исходной задачи.
    • target - (число) ID целевой задачи.
    • type - (строка) тип зависимости:
      • 0 - 'finish_to_start'
      • 1 - 'start_to_start'
      • 2 - 'finish_to_finish'
      • 3 - 'start_to_finish'
    • lag - (число) необязательно, задержка задачи.
    • readonly - (булево) необязательно, помечает связь как только для чтения.
    • editable - (булево) необязательно, помечает связь как редактируемую.

Вот SQL-запрос для создания необходимых таблиц базы данных:

CREATE TABLE `gantt_links` (
  `id` INT(11) NOT NULL AUTO_INCREMENT,
  `source` INT(11) NOT NULL,
  `target` INT(11) NOT NULL,
  `type` VARCHAR(1) NOT NULL,
  PRIMARY KEY (`id`)
);
CREATE TABLE `gantt_tasks` (
  `id` INT(11) NOT NULL AUTO_INCREMENT,
  `text` VARCHAR(255) NOT NULL,
  `start_date` datetime NOT NULL,
  `duration` INT(11) NOT NULL,
  `progress` FLOAT NOT NULL,
  `sortorder` INT(11) NOT NULL,
  `parent` INT(11) NOT NULL,
  PRIMARY KEY (`id`)
);

Последовательность событий

Вот последовательность событий для методов загрузки данных:

gantt.parse():

gantt.load()

gantt.refreshData():

gantt.render():

К началу