There are several ways of implementing tasks moving with their dependent tasks.
Firstly, you can make use of the Auto Scheduling extension. It allows scheduling tasks automatically depending on relations between them.
To use the auto scheduling functionality, you should enable it using the gantt.plugins method:
gantt.plugins({
auto_scheduling: true
});
And set the auto_scheduling property to true:
gantt.config.auto_scheduling = true;
The common approach with dragging dependent tasks is the following:
So, you can choose one of the two ways:
In both cases, you need to get all linked tasks first.
To retrieve the task related links, use the $source and $target properties of the task's object. The properties are autogenerated and store ids of the related links:
var taskObj = gantt.getTask("t1");
var sourceLinks = taskObj.$source; //-> ["l1","l4"] - ids of coming-out links
var targetLinks = taskObj.$target; //-> ["l5","l8"] - ids of coming-into links
and from the links you can get the dependent tasks.
So, to get the linked tasks, we need to declare an iterator:
gantt.eachSuccessor = function(callback, root){
if(!this.isTaskExists(root))
return;
// remember tasks we've already iterated in order to avoid infinite loops
var traversedTasks = arguments[2] || {};
if(traversedTasks[root])
return;
traversedTasks[root] = true;
var rootTask = this.getTask(root);
var links = rootTask.$source;
if(links){
for(var i=0; i < links.length; i++){
var link = this.getLink(links[i]);
if(this.isTaskExists(link.target) && !traversedTasks[link.target]){
callback.call(this, this.getTask(link.target));
// iterate the whole branch, not only first-level dependencies
this.eachSuccessor(callback, link.target, traversedTasks);
}
}
}
};
Descendant tasks can be moved synchronously with the moving of the main tasks, i.e. when the user starts moving tasks, all dependent branches will be moved together. It will look good, but the downside is that there may be a performance drop, if you are moving many tasks at the same time.
Firstly, we will declare the iterator, as it's shown above.
Then, you need to attach a handler to the onTaskDrag event. It will be called on each frame of drag and drop, and from here we'll move all linked tasks.
gantt.attachEvent("onTaskDrag", function(id, mode, task, original){
var modes = gantt.config.drag_mode;
if(mode == modes.move){
var diff = task.start_date - original.start_date;
gantt.eachSuccessor(function(child){
child.start_date = new Date(+child.start_date + diff);
child.end_date = new Date(+child.end_date + diff);
gantt.refreshTask(child.id, true);
},id );
}
return true;
});
Finally, when the user releases the mouse and drag-and-drop is finished, we need to round positions of the child items to scale. We can do it using onAfterTaskDrag event:
gantt.attachEvent("onAfterTaskDrag", function(id, mode, e){
var modes = gantt.config.drag_mode;
if(mode == modes.move ){
gantt.eachSuccessor(function(child){
child.start_date = gantt.roundDate(child.start_date);
child.end_date = gantt.calculateEndDate(child.start_date, child.duration);
gantt.updateTask(child.id);
},id );
}
});
This approach works fine if you don't have too many linked tasks.
Descendant tasks can be updated after the user finishes moving the main task. The result will look simpler, but have a better performance.
The approach is the following: when the drag and drop is finished, we check what amount the task has been moved for, and move all linked tasks to the same value.
Firstly, we will declare the iterator, as it's shown above.
When the user releases the mouse and drag and drop is finished, we can capture the onBeforeTaskChanged event, where both the modified and the original instances of the moved task are available and calculate date difference between them.
Note, that at this stage drag-and-drop can be canceled (since onBeforeTaskChanged allows canceling it, and your app may have handlers that can do it), so we don't modify dependent tasks here.
Instead, we'll put the calculated diff value in a variable in the same closure, so that it could be accessed later.
var diff = 0;
gantt.attachEvent("onBeforeTaskChanged", function(id, mode, originalTask){
var modes = gantt.config.drag_mode;
if(mode == modes.move ){
var modifiedTask = gantt.getTask(id);
diff = modifiedTask.start_date - originalTask.start_date;
}
return true;
});
Finally, we capture the onAfterTaskDrag event, which tells that drag-and-drop has been performed. At this point we can update all dependent tasks using diff we've calculated at the previous step:
//rounds positions of the child items to scale
gantt.attachEvent("onAfterTaskDrag", function(id, mode, e){
var modes = gantt.config.drag_mode;
if(mode == modes.move ){
gantt.eachSuccessor(function(child){
child.start_date = gantt.roundDate(new Date(child.start_date.valueOf() + diff));
child.end_date = gantt.calculateEndDate(child.start_date, child.duration);
gantt.updateTask(child.id);
},id );
}
});
The full code will be as follows:
(function(){
var diff = 0;
gantt.attachEvent("onBeforeTaskChanged", function(id, mode, originalTask){
var modes = gantt.config.drag_mode;
if(mode == modes.move ){
var modifiedTask = gantt.getTask(id);
diff = modifiedTask.start_date - originalTask.start_date;
}
return true;
});
//rounds positions of the child items to scale
gantt.attachEvent("onAfterTaskDrag", function(id, mode, e){
var modes = gantt.config.drag_mode;
if(mode == modes.move ){
gantt.eachSuccessor(function(child){
child.start_date = gantt.roundDate(new Date(child.start_date.valueOf() + diff));
child.end_date = gantt.calculateEndDate(child.start_date, child.duration);
gantt.updateTask(child.id);
},id );
}
});
})();
Back to top