Task dependency in Angular Gantt component
18 Oct 202524 minutes to read
Task dependency in the Angular Gantt component establishes relationships between tasks, affecting scheduling where changes to predecessors impact successors. Dependencies are categorized into four types—Start to Start (SS), Start to Finish (SF), Finish to Start (FS), and Finish to Finish (FF)—mapped via the taskFields.dependency property in the data source. Parent dependencies are enabled by default with allowParentDependency set to true, allowing relationships between parent-parent, child-child, parent-child, and child-parent tasks. Offsets support day, hour, or minute units for precise timing, and validation modes handle conflicts during editing via the actionBegin event. Connector lines are customized using connectorLineWidth and connectorLineBackground, with the queryTaskbarInfo event enabling dynamic styling. Public methods like addPredecessor and removePredecessor allow programmatic management, ensuring accurate visualization with ARIA labels for accessibility and responsive scaling for mobile views.
Configure task dependencies
Task dependencies are defined in the data source as string values (e.g., ‘2FS+3d’ for Finish to Start with 3-day offset) and mapped using taskFields.dependency. Parent dependencies can be enabled by allowParentDependency property. By default, the allowParentDependency property will be true.
The following example establishes dependencies:
import { GanttModule } from '@syncfusion/ej2-angular-gantt'
import { Component, ViewEncapsulation, OnInit } from '@angular/core';
@Component({
imports: [GanttModule],
standalone: true,
selector: 'app-root',
template:
`<ejs-gantt height="430px" [dataSource]="data" [taskFields]="taskSettings"></ejs-gantt>`,
encapsulation: ViewEncapsulation.None
})
export class AppComponent implements OnInit {
public data?: object[];
public taskSettings?: object;
public ngOnInit(): void {
this.data = [
{
TaskID: 1,
TaskName: 'Project Initiation',
StartDate: new Date('04/02/2019'),
EndDate: new Date('04/21/2019'),
subtasks: [
{ TaskID: 2, TaskName: 'Identify Site location', StartDate: new Date('04/02/2019'), Duration: 0, Progress: 50 },
{ TaskID: 3, TaskName: 'Perform Soil test', StartDate: new Date('04/02/2019'), Duration: 4, Progress: 50 },
{ TaskID: 4, TaskName: 'Soil test approval', StartDate: new Date('04/02/2019'), Duration: 4, Predecessor: "2FS", Progress: 50 },
]
},
{
TaskID: 5,
TaskName: 'Project Estimation',
StartDate: new Date('04/02/2019'),
EndDate: new Date('04/21/2019'),
subtasks: [
{ TaskID: 6, TaskName: 'Develop floor plan for estimation', StartDate: new Date('04/04/2019'), Duration: 3, Progress: 50 },
{ TaskID: 7, TaskName: 'List materials', StartDate: new Date('04/04/2019'), Duration: 3, Progress: 50 },
{ TaskID: 8, TaskName: 'Estimation approval', StartDate: new Date('04/04/2019'), Duration: 0, Predecessor: "6SS", Progress: 50 }
]
},
];
this.taskSettings = {
id: 'TaskID',
name: 'TaskName',
startDate: 'StartDate',
endDate: 'EndDate',
duration: 'Duration',
progress: 'Progress',
dependency: 'Predecessor',
child: 'subtasks'
};
}
}import { bootstrapApplication } from '@angular/platform-browser';
import { AppComponent } from './app.component';
import 'zone.js';
bootstrapApplication(AppComponent).catch((err) => console.error(err));This code renders connector lines for dependencies like ‘2FS’, updating taskbars on changes.
Understand task relationship types
Task relationships are categorized into four types based on start and finish dates:
-
Start to Start (SS): Successor starts with predecessor.

-
Start to Finish (SF): Successor finishes when predecessor starts.

-
Finish to Start (FS): Successor starts after predecessor finishes (default).

-
Finish to Finish (FF): Successor finishes with predecessor.

Specify types in the data source (e.g., ‘2SS+1h’) for hour-based offsets.
Configure predecessor offsets with duration units
Predecessor offsets support day, hour, or minute units (e.g., ‘2FS+3h’), allowing precise delays or leads between tasks.
The following example uses duration units:
import { Component, ViewEncapsulation, OnInit } from '@angular/core';
import { GanttModule } from '@syncfusion/ej2-angular-gantt'
@Component({
imports: [GanttModule],
standalone: true,
selector: 'app-root',
template:
`<ejs-gantt height="430px" [dataSource]="data" [taskFields]="taskSettings" [columns]="columns"></ejs-gantt>`,
encapsulation: ViewEncapsulation.None
})
export class AppComponent implements OnInit {
public data?: object[];
public taskSettings?: object;
public columns?: object[];
public ngOnInit(): void {
this.data = [
{
TaskID: 1,
TaskName: 'Project Initiation',
StartDate: new Date('04/02/2019'),
EndDate: new Date('04/21/2019'),
subtasks: [
{ TaskID: 2, TaskName: 'Identify Site location', StartDate: new Date('04/02/2019'), Duration: 0, Progress: 50 },
{ TaskID: 3, TaskName: 'Perform Soil test', StartDate: new Date('04/02/2019'), Duration: 4, Progress: 50 },
{ TaskID: 4, TaskName: 'Soil test approval', StartDate: new Date('04/04/2019'), Duration: 4, Predecessor: "2FS+2days", Progress: 50 },
{ TaskID: 5, TaskName: 'Clear the building site', StartDate: new Date('04/04/2019'), Duration: 2, Progress: 30, Predecessor: '4FF+960m' }
]
},
{
TaskID: 6,
TaskName: 'Project Estimation',
StartDate: new Date('04/02/2019'),
EndDate: new Date('04/21/2019'),
subtasks: [
{ TaskID: 7, TaskName: 'Develop floor plan for estimation', StartDate: new Date('04/04/2019'), Duration: 3, Progress: 50 },
{ TaskID: 8, TaskName: 'List materials', StartDate: new Date('04/04/2019'), Duration: 3, Progress: 50 },
{ TaskID: 9, TaskName: 'Estimation approval', StartDate: new Date('04/06/2019'), Duration: 0, Predecessor: "7SS+16h", Progress: 50 }
]
},
];
this.taskSettings = {
id: 'TaskID',
name: 'TaskName',
startDate: 'StartDate',
endDate: 'EndDate',
duration: 'Duration',
progress: 'Progress',
dependency: 'Predecessor',
child: 'subtasks'
};
this.columns = [
{ field: 'TaskID', headerText: 'Task ID', textAlign: 'Left', width: '100' },
{ field: 'Predecessor', headerText: 'Depedency', width: '150' },
{ field: 'TaskName', headerText: 'Task Name', width: '150' },
{ field: 'StartDate', headerText: 'Start Date', width: '150' },
{ field: 'Duration', headerText: 'Duration', width: '150' },
{ field: 'Progress', headerText: 'Progress', width: '150' }
];
}
}import { bootstrapApplication } from '@angular/platform-browser';
import { AppComponent } from './app.component';
import 'zone.js';
bootstrapApplication(AppComponent).catch((err) => console.error(err));This code sets offsets like ‘2FS+3h’, adjusting taskbars accordingly.
Disable automatic dependency offset updates
Automatic offset updates during taskbar editing are disabled with updateOffsetOnTaskbarEdit set to false, allowing manual updates via the dependency tab or predecessor column.
The following example disables automatic updates:
import { Component, ViewEncapsulation, OnInit } from '@angular/core';
import { GanttModule, EditSettingsModel } from '@syncfusion/ej2-angular-gantt';
@Component({
imports: [GanttModule],
standalone: true,
selector: 'app-root',
template:
`<ejs-gantt height="430px" [dataSource]="data" [editSettings]="editSettings" [taskFields]="taskSettings" [columns]="columns"></ejs-gantt>`,
encapsulation: ViewEncapsulation.None
})
export class AppComponent implements OnInit {
public data?: object[];
public taskSettings?: object;
public editSettings?: EditSettingsModel;
public columns?: object[];
public ngOnInit(): void {
this.data = [
{
TaskID: 1,
TaskName: 'Project Initiation',
StartDate: new Date('04/02/2019'),
EndDate: new Date('04/21/2019'),
subtasks: [
{ TaskID: 2, TaskName: 'Identify Site location', StartDate: new Date('04/02/2019'), Duration: 0, Progress: 50 },
{ TaskID: 3, TaskName: 'Perform Soil test', StartDate: new Date('04/02/2019'), Duration: 4, Progress: 50 },
{ TaskID: 4, TaskName: 'Soil test approval', StartDate: new Date('04/04/2019'), Duration: 4, Predecessor: "2FS+2days", Progress: 50 },
{ TaskID: 5, TaskName: 'Clear the building site', StartDate: new Date('04/04/2019'), Duration: 2, Progress: 30, Predecessor: '4FF+960m' }
]
},
{
TaskID: 6,
TaskName: 'Project Estimation',
StartDate: new Date('04/02/2019'),
EndDate: new Date('04/21/2019'),
subtasks: [
{ TaskID: 7, TaskName: 'Develop floor plan for estimation', StartDate: new Date('04/04/2019'), Duration: 3, Progress: 50 },
{ TaskID: 8, TaskName: 'List materials', StartDate: new Date('04/04/2019'), Duration: 3, Progress: 50 },
{ TaskID: 9, TaskName: 'Estimation approval', StartDate: new Date('04/06/2019'), Duration: 0, Predecessor: "7SS+16h", Progress: 50 }
]
},
];
this.taskSettings = {
id: 'TaskID',
name: 'TaskName',
startDate: 'StartDate',
endDate: 'EndDate',
duration: 'Duration',
progress: 'Progress',
dependency: 'Predecessor',
child: 'subtasks'
};
this.columns = [
{ field: 'TaskID', headerText: 'Task ID', textAlign: 'Left', width: '100' },
{ field: 'Predecessor', headerText: 'Depedency', width: '150' },
{ field: 'TaskName', headerText: 'Task Name', width: '150' },
{ field: 'StartDate', headerText: 'Start Date', width: '150' },
{ field: 'Duration', headerText: 'Duration', width: '150' },
{ field: 'Progress', headerText: 'Progress', width: '150' }
];
this.editSettings = {
allowAdding: true,
allowEditing: true,
allowDeleting: true,
allowTaskbarEditing: true,
showDeleteConfirmDialog: true
}
}
}import { bootstrapApplication } from '@angular/platform-browser';
import { AppComponent } from './app.component';
import 'zone.js';
bootstrapApplication(AppComponent).catch((err) => console.error(err));This code preserves offsets during edits, requiring manual adjustments.
Handle dependency validation modes
Dependency validation during editing uses the actionBegin event with requestType: 'validateLinkedTask'. The validateMode argument defines modes:
-
respectLink: Prioritizes links, reverting invalid edits. -
removeLink: Prioritizes editing, removing conflicting links. -
preserveLinkWithEditing: Updates offsets to maintain links (default).
The following example enables respectLink mode:
import { Component, ViewEncapsulation, OnInit } from '@angular/core';
import { GanttModule, EditSettingsModel, EditService, ActionBeginArgs } from '@syncfusion/ej2-angular-gantt';
@Component({
imports: [GanttModule],
providers: [EditService],
standalone: true,
selector: 'app-root',
template:
`<ejs-gantt id="ganttDefault" height="430px" [dataSource]="data" [taskFields]="taskSettings" [columns]="columns" [editSettings]="editSettings" (actionBegin)="actionBegin($event)"></ejs-gantt>`,
encapsulation: ViewEncapsulation.None
})
export class AppComponent implements OnInit {
public data?: object[];
public taskSettings?: object;
public editSettings?: EditSettingsModel;
public columns?: object[];
public ngOnInit(): void {
this.data = [
{
TaskID: 1,
TaskName: 'Project Initiation',
StartDate: new Date('04/02/2019'),
EndDate: new Date('04/21/2019'),
subtasks: [
{ TaskID: 2, TaskName: 'Identify Site location', StartDate: new Date('04/02/2019'), Duration: 0, Progress: 50 },
{ TaskID: 3, TaskName: 'Perform Soil test', StartDate: new Date('04/02/2019'), Duration: 4, Progress: 50 },
{ TaskID: 4, TaskName: 'Soil test approval', StartDate: new Date('04/02/2019'), Duration: 4, Predecessor: "2FS", Progress: 50 },
]
},
{
TaskID: 5,
TaskName: 'Project Estimation',
StartDate: new Date('04/02/2019'),
EndDate: new Date('04/21/2019'),
subtasks: [
{ TaskID: 6, TaskName: 'Develop floor plan for estimation', StartDate: new Date('04/04/2019'), Duration: 3, Progress: 50 },
{ TaskID: 7, TaskName: 'List materials', StartDate: new Date('04/04/2019'), Duration: 3, Progress: 50 },
{ TaskID: 8, TaskName: 'Estimation approval', StartDate: new Date('04/04/2019'), Duration: 0, Predecessor: "6SS", Progress: 50 }
]
},
];
this.taskSettings = {
id: 'TaskID',
name: 'TaskName',
startDate: 'StartDate',
endDate: 'EndDate',
duration: 'Duration',
progress: 'Progress',
dependency: 'Predecessor',
child: 'subtasks'
};
this.editSettings = {
allowTaskbarEditing: true
};
this.columns = [
{ field: 'TaskID', headerText: 'Task ID', textAlign: 'Left', width: '100' },
{ field: 'TaskName', headerText: 'Task Name', width: '150' },
{ field: 'StartDate', headerText: 'Start Date', width: '150' },
{ field: 'Duration', headerText: 'Duration', width: '150' },
{ field: 'Predecessor', headerText: 'Depedency', width: '150' },
{ field: 'Progress', headerText: 'Progress', width: '150' }
];
}
public actionBegin(args: ActionBeginArgs) {
if (args.requestType == "validateLinkedTask") {
(args as any).validateMode.respectLink = true;
}
};
}import { bootstrapApplication } from '@angular/platform-browser';
import { AppComponent } from './app.component';
import 'zone.js';
bootstrapApplication(AppComponent).catch((err) => console.error(err));This code reverts edits violating links, ensuring dependency integrity.
Use validation dialog
When all validation modes are disabled in actionBegin, a dialog prompts users to choose modes like canceling edits or removing links, based on the successor’s start date relative to the predecessor.
The following example enables the validation dialog:
import { Component, ViewEncapsulation, OnInit } from '@angular/core';
import { GanttModule, EditService, EditSettingsModel, IDependencyEventArgs, ActionBeginArgs } from '@syncfusion/ej2-angular-gantt'
@Component({
imports: [GanttModule],
providers: [EditService],
standalone: true,
selector: 'app-root',
template:
`<ejs-gantt id="ganttDefault" height="430px" [dataSource]="data" [taskFields]="taskSettings" [columns]="columns" [editSettings]="editSettings" (actionBegin)="actionBegin($event)"></ejs-gantt>`,
encapsulation: ViewEncapsulation.None
})
export class AppComponent implements OnInit {
public data?: object[];
public taskSettings?: object;
public editSettings?: EditSettingsModel;
public columns?: object[];
public ngOnInit(): void {
this.data = [
{
TaskID: 1,
TaskName: 'Project Initiation',
StartDate: new Date('04/02/2019'),
EndDate: new Date('04/21/2019'),
subtasks: [
{ TaskID: 2, TaskName: 'Identify Site location', StartDate: new Date('04/02/2019'), Duration: 0, Progress: 50 },
{ TaskID: 3, TaskName: 'Perform Soil test', StartDate: new Date('04/02/2019'), Duration: 4, Progress: 50 },
{ TaskID: 4, TaskName: 'Soil test approval', StartDate: new Date('04/02/2019'), Duration: 4, Predecessor: "2FS", Progress: 50 },
]
},
{
TaskID: 5,
TaskName: 'Project Estimation',
StartDate: new Date('04/02/2019'),
EndDate: new Date('04/21/2019'),
subtasks: [
{ TaskID: 6, TaskName: 'Develop floor plan for estimation', StartDate: new Date('04/04/2019'), Duration: 3, Progress: 50 },
{ TaskID: 7, TaskName: 'List materials', StartDate: new Date('04/04/2019'), Duration: 3, Progress: 50 },
{ TaskID: 8, TaskName: 'Estimation approval', StartDate: new Date('04/04/2019'), Duration: 0, Predecessor: "6SS", Progress: 50 }
]
},
];
this.taskSettings = {
id: 'TaskID',
name: 'TaskName',
startDate: 'StartDate',
endDate: 'EndDate',
duration: 'Duration',
progress: 'Progress',
dependency: 'Predecessor',
child: 'subtasks'
};
this.editSettings = {
allowTaskbarEditing: true
},
this.columns = [
{ field: 'TaskID', headerText: 'Task ID', textAlign: 'Left', width: '100' },
{ field: 'Predecessor', headerText: 'Depedency', width: '150' },
{ field: 'TaskName', headerText: 'Task Name', width: '150' },
{ field: 'StartDate', headerText: 'Start Date', width: '150' },
{ field: 'Duration', headerText: 'Duration', width: '150' },
{ field: 'Progress', headerText: 'Progress', width: '150' }
];
}
public actionBegin(args: ActionBeginArgs) {
if (args.requestType == "validateLinkedTask") {
(args as any).validateMode.preserveLinkWithEditing = false;
}
};
}import { bootstrapApplication } from '@angular/platform-browser';
import { AppComponent } from './app.component';
import 'zone.js';
bootstrapApplication(AppComponent).catch((err) => console.error(err));This code displays options like “Remove the link and move the task” for conflicts.
Show or hide dependency lines dynamically
Dependency lines are hidden or shown by toggling visibility: hidden on .e-gantt-dependency-view-container, allowing dynamic control for focused views.
The following example toggles dependency lines:
import { Component, ViewEncapsulation, OnInit, ViewChild } from '@angular/core';
import { GanttModule, EditSettingsModel } from '@syncfusion/ej2-angular-gantt';
import { SwitchModule, SwitchComponent } from '@syncfusion/ej2-angular-buttons'
@Component({
imports: [GanttModule, SwitchModule],
standalone: true,
selector: 'app-root',
template: `<label>Show/Hide Dependency Line</label><ejs-switch #switch onLabel="ON" [(checked)]="checked" offLabel="OFF" (change)='change()'></ejs-switch>
<ejs-gantt id="ganttDefault" height="430px" [dataSource]="data" [editSettings] = "editSettings" [taskFields]="taskSettings" [columns]="columns"></ejs-gantt>`,
encapsulation: ViewEncapsulation.None,
})
export class AppComponent implements OnInit {
@ViewChild('switch') public switch: SwitchComponent;
public checked: boolean = false;
public data?: object[];
public taskSettings?: object;
public editSettings?: EditSettingsModel;
public columns?: object[];
public ngOnInit(): void {
this.data = [
{
TaskID: 1,
TaskName: 'Project Initiation',
StartDate: new Date('04/02/2019'),
EndDate: new Date('04/21/2019'),
subtasks: [
{ TaskID: 2, TaskName: 'Identify Site location', StartDate: new Date('04/02/2019'), Duration: 0, Progress: 50, },
{ TaskID: 3, TaskName: 'Perform Soil test', StartDate: new Date('04/02/2019'), Duration: 4, Progress: 50, },
{ TaskID: 4, TaskName: 'Soil test approval', StartDate: new Date('04/04/2019'), Duration: 4, Predecessor: '2FS+2days', Progress: 50, },
{ TaskID: 5, TaskName: 'Clear the building site', StartDate: new Date('04/04/2019'), Duration: 2, Progress: 30, Predecessor: '4FF+960m', },
],
},
{
TaskID: 6,
TaskName: 'Project Estimation',
StartDate: new Date('04/02/2019'),
EndDate: new Date('04/21/2019'),
subtasks: [
{ TaskID: 7, TaskName: 'Develop floor plan for estimation', StartDate: new Date('04/04/2019'), Duration: 3, Progress: 50, },
{ TaskID: 8, TaskName: 'List materials', StartDate: new Date('04/04/2019'), Duration: 3, Progress: 50, },
{ TaskID: 9, TaskName: 'Estimation approval', StartDate: new Date('04/06/2019'), Duration: 0, Predecessor: '7SS+16h', Progress: 50, },
],
},
];
this.taskSettings = {
id: 'TaskID',
name: 'TaskName',
startDate: 'StartDate',
endDate: 'EndDate',
duration: 'Duration',
progress: 'Progress',
dependency: 'Predecessor',
child: 'subtasks',
};
this.columns = [
{
field: 'TaskID',
headerText: 'Task ID',
textAlign: 'Left',
width: '100',
},
{ field: 'Predecessor', headerText: 'Depedency', width: '150' },
{ field: 'TaskName', headerText: 'Task Name', width: '150' },
{ field: 'StartDate', headerText: 'Start Date', width: '150' },
{ field: 'Duration', headerText: 'Duration', width: '150' },
{ field: 'Progress', headerText: 'Progress', width: '150' },
];
this.editSettings = {
allowAdding: true,
allowEditing: true,
allowDeleting: true,
allowTaskbarEditing: true,
showDeleteConfirmDialog: true,
};
}
public change() {
const ganttDependencyViewContainer = document.querySelector('.e-gantt-dependency-view-container') as HTMLElement;
if (this.checked) {
ganttDependencyViewContainer.style.visibility = 'hidden';
} else {
ganttDependencyViewContainer.style.visibility = 'visible';
}
}
}import { bootstrapApplication } from '@angular/platform-browser';
import { AppComponent } from './app.component';
import 'zone.js';
bootstrapApplication(AppComponent).catch((err) => console.error(err));This code hides lines on button click, with ARIA updates for accessibility.
Customize connector lines
Connector lines are styled globally with connectorLineWidth and connectorLineBackground.
The following example sets the connector line background color as red:
import { Component } from '@angular/core';
import { GanttModule } from '@syncfusion/ej2-angular-gantt';
@Component({
imports: [GanttModule],
standalone: true,
selector: 'app-root',
template: `
<ejs-gantt height="430px" [dataSource]="taskData" [taskFields]="taskSettings" [projectStartDate]="projectStartDate" [projectEndDate]="projectEndDate" [connectorLineWidth]="2" [connectorLineBackground]="'red'">
</ejs-gantt>`
})
export class AppComponent {
public taskData: object[] = [
{ TaskID: 1, TaskName: "Product concept", StartDate: new Date("04/02/2025"), EndDate: new Date("04/08/2025") },
{ TaskID: 2, TaskName: "Define the product usage", StartDate: new Date("04/02/2025"), EndDate: new Date("04/08/2025"), Duration: 1, Progress: 30, ParentId: 1 },
{ TaskID: 3, TaskName: "Define the target audience", StartDate: new Date("04/02/2025"), EndDate: new Date("04/04/2025"), Duration: 2, Progress: 40, ParentId: 1 },
{ TaskID: 4, TaskName: "Prepare product sketch and notes", StartDate: new Date("04/05/2025"), Duration: 2, Progress: 30, ParentId: 1, Predecessor: "2" },
{ TaskID: 5, TaskName: "Concept approval", StartDate: new Date("04/08/2025"), EndDate: new Date("04/08/2025"), Duration: 0, ParentId: 1, Predecessor: "3,4" },
{ TaskID: 6, TaskName: "Market research", StartDate: new Date("04/09/2025"), EndDate: new Date("04/18/2025"), Progress: 30 },
{ TaskID: 7, TaskName: "Demand analysis", Progress: 40, ParentId: 6 },
{ TaskID: 8, TaskName: "Customer strength", StartDate: new Date("04/09/2025"), EndDate: new Date("04/12/2025"), Duration: 4, Progress: 30, ParentId: 7, Predecessor: "5"},
{ TaskID: 9, TaskName: "Market opportunity analysis", StartDate: new Date("04/09/2025"), EndDate: new Date("04/12/2025"), Duration: 4, ParentId: 7, Predecessor: "5" },
{ TaskID: 10, TaskName: "Competitor analysis", StartDate: new Date("04/15/2025"), EndDate: new Date("04/18/2025"), Duration: 4, Progress: 30, ParentId: 6, Predecessor: "7,8" },
];
public taskSettings: object = {
id: 'TaskID',
name: 'TaskName',
startDate: 'StartDate',
duration: 'Duration',
progress: 'Progress',
dependency: 'Predecessor',
parentID: 'ParentId'
};
}