Data binding in React Gantt component
23 Jan 202424 minutes to read
The Gantt component uses DataManager
for binding the data source, which supports both RESTful JSON data services and local JavaScript object array. The dataSource
property can be assigned either with the instance of DataManager or JavaScript object array collection. The Gantt component supports binding two types of data:
- Local data
- Remote data
The following video explains about the types of data binding and how to use it on the Gantt Chart component:
Local data
To bind local data to Gantt, you can assign a JavaScript object array to the dataSource
property. The local data source can also be provided as an instance of the DataManager
.
In local data binding, the data source for rendering the Gantt component is retrieved from the same application locally.
The following are the two types of data binding possible with the Gantt component:
- Hierarchical data binding.
- Self-referential data binding (Flat data).
Hierarchical data binding
The child
property is used to map the child records in hierarchical data.
The following code example shows how to bind the hierarchical local data into the Gantt component.
let HierarchyData = [
{
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: 4, 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, 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: 3, Progress: 50 }
]
}
];
import * as React from 'react';
import * as ReactDOM from 'react-dom';
import { GanttComponent } from '@syncfusion/ej2-react-gantt';
function App (){
const taskFields = {
id: 'TaskID',
name: 'TaskName',
startDate: 'StartDate',
duration: 'Duration',
progress: 'Progress',
child: 'subtasks'
};
return <GanttComponent dataSource={HierarchyData} taskFields={taskFields} height = '400px'>
</GanttComponent>
};
ReactDOM.render(<App />, document.getElementById('root'));
let HierarchyData: Object[] = [
{
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: 4, 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, 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: 3, Progress: 50 }
]
}
];
import * as React from 'react';
import * as ReactDOM from 'react-dom';
import { GanttComponent } from '@syncfusion/ej2-react-gantt';
function App (){
const taskFields: any = {
id: 'TaskID',
name: 'TaskName',
startDate: 'StartDate',
duration: 'Duration',
progress: 'Progress',
child: 'subtasks'
};
return <GanttComponent dataSource={HierarchyData} taskFields={taskFields} height = '400px'>
</GanttComponent>
};
ReactDOM.render(<App />, document.getElementById('root'));
<!DOCTYPE html>
<html lang="en">
<head>
<title>Syncfusion React Gantt</title>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta name="description" content="Essential JS 2 for React Components" />
<meta name="author" content="Syncfusion" />
<link href="https://cdn.syncfusion.com/ej2/27.2.2/material.css" rel="stylesheet" type="text/css"/>
<link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet" />
<script src="https://cdnjs.cloudflare.com/ajax/libs/systemjs/0.19.38/system.js"></script>
<script src="systemjs.config.js"></script>
<style>
#loader {
color: #008cff;
height: 40px;
left: 45%;
position: absolute;
top: 45%;
width: 30%;
}
</style>
<script src="https://cdn.syncfusion.com/ej2/syncfusion-helper.js" type ="text/javascript"></script>
</head>
<body>
<div id='root'>
<div id='loader'>Loading....</div>
</div>
</body>
</html>
Self-referential data binding (Flat data)
The Gantt component can be bound with self-referential data by mapping the data source field values to the id
and parentID
properties.
- ID field: This field contains unique values used to identify each individual task and it is mapped to the
id
property. - Parent ID field: This field contains values that indicate parent tasks and it is mapped to the
parentID
property.
let SelfReferenceData = [
{ TaskID: 1,TaskName: 'Project Initiation',StartDate: new Date('04/02/2019'),EndDate: new Date('04/21/2019')},
{ TaskID: 2, TaskName: 'Identify Site location', StartDate: new Date('04/02/2019'), Duration: 4, Progress: 50,ParentId:1 },
{ TaskID: 3, TaskName: 'Perform Soil test', StartDate: new Date('04/02/2019'), Duration: 4, Progress: 50, ParentId:1 },
{ TaskID: 4, TaskName: 'Soil test approval', StartDate: new Date('04/02/2019'), Duration: 4, Progress: 50,ParentId:1 },
{ TaskID: 5, TaskName: 'Project Estimation',StartDate: new Date('04/02/2019'),EndDate: new Date('04/21/2019')},
{ TaskID: 6, TaskName: 'Develop floor plan for estimation', StartDate: new Date('04/04/2019'), Duration: 3, Progress: 50, ParentId:5 },
{ TaskID: 7, TaskName: 'List materials', StartDate: new Date('04/04/2019'), Duration: 3, Progress: 50,ParentId:5 },
{ TaskID: 8, TaskName: 'Estimation approval', StartDate: new Date('04/04/2019'), Duration: 3, Progress: 50, ParentId:5 }
];
import * as React from 'react';
import * as ReactDOM from 'react-dom';
import { GanttComponent } from '@syncfusion/ej2-react-gantt';
function App (){
const taskFields = {
id: 'TaskID',
name: 'TaskName',
startDate: 'StartDate',
duration: 'Duration',
progress: 'Progress',
parentID: 'ParentId'
};
return <GanttComponent dataSource={SelfReferenceData} taskFields={taskFields} height = '450px'>
</GanttComponent>
};
ReactDOM.render(<App />, document.getElementById('root'));
let SelfReferenceData: Object[] = [
{ TaskID: 1,TaskName: 'Project Initiation',StartDate: new Date('04/02/2019'),EndDate: new Date('04/21/2019')},
{ TaskID: 2, TaskName: 'Identify Site location', StartDate: new Date('04/02/2019'), Duration: 4, Progress: 50,ParentId:1 },
{ TaskID: 3, TaskName: 'Perform Soil test', StartDate: new Date('04/02/2019'), Duration: 4, Progress: 50, ParentId:1 },
{ TaskID: 4, TaskName: 'Soil test approval', StartDate: new Date('04/02/2019'), Duration: 4, Progress: 50,ParentId:1 },
{ TaskID: 5, TaskName: 'Project Estimation',StartDate: new Date('04/02/2019'),EndDate: new Date('04/21/2019')},
{ TaskID: 6, TaskName: 'Develop floor plan for estimation', StartDate: new Date('04/04/2019'), Duration: 3, Progress: 50, ParentId:5 },
{ TaskID: 7, TaskName: 'List materials', StartDate: new Date('04/04/2019'), Duration: 3, Progress: 50,ParentId:5 },
{ TaskID: 8, TaskName: 'Estimation approval', StartDate: new Date('04/04/2019'), Duration: 3, Progress: 50, ParentId:5 }
];
import * as React from 'react';
import * as ReactDOM from 'react-dom';
import { GanttComponent } from '@syncfusion/ej2-react-gantt';
function App (){
const taskFields: any = {
id: 'TaskID',
name: 'TaskName',
startDate: 'StartDate',
duration: 'Duration',
progress: 'Progress',
parentID: 'ParentId'
};
return <GanttComponent dataSource={SelfReferenceData} taskFields={taskFields} height = '450px'>
</GanttComponent>
};
ReactDOM.render(<App />, document.getElementById('root'));
<!DOCTYPE html>
<html lang="en">
<head>
<title>Syncfusion React Gantt</title>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta name="description" content="Essential JS 2 for React Components" />
<meta name="author" content="Syncfusion" />
<link href="https://cdn.syncfusion.com/ej2/27.2.2/material.css" rel="stylesheet" type="text/css"/>
<link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet" />
<script src="https://cdnjs.cloudflare.com/ajax/libs/systemjs/0.19.38/system.js"></script>
<script src="systemjs.config.js"></script>
<style>
#loader {
color: #008cff;
height: 40px;
left: 45%;
position: absolute;
top: 45%;
width: 30%;
}
</style>
<script src="https://cdn.syncfusion.com/ej2/syncfusion-helper.js" type ="text/javascript"></script>
</head>
<body>
<div id='root'>
<div id='loader'>Loading....</div>
</div>
</body>
</html>
Remote data
To bind remote data to the Gantt component, assign service data as an instance of DataManager
to the dataSource
property.
import * as React from 'react';
import * as ReactDOM from 'react-dom';
import { GanttComponent } from '@syncfusion/ej2-react-gantt';
import { DataManager, WebApiAdaptor } from '@syncfusion/ej2-data';
function App (){
const taskFields = {
id: 'TaskId',
name: 'TaskName',
startDate: 'StartDate',
duration: 'Duration',
dependency: 'Predecessor',
child: 'SubTasks'
};
const dataSource = new DataManager({
url: 'https://services.syncfusion.com/react/production/api/GanttData',
adaptor: new WebApiAdaptor,
crossDomain: true
});
return <GanttComponent dataSource={dataSource} taskFields={taskFields} height = '450px'>
</GanttComponent>
};
ReactDOM.render(<App />, document.getElementById('root'));
import * as React from 'react';
import * as ReactDOM from 'react-dom';
import { GanttComponent } from '@syncfusion/ej2-react-gantt';
import { DataManager, WebApiAdaptor } from '@syncfusion/ej2-data';
function App (){
const taskFields: any = {
id: 'TaskId',
name: 'TaskName',
startDate: 'StartDate',
duration: 'Duration',
dependency: 'Predecessor',
child: 'SubTasks'
};
const dataSource: DataManager = new DataManager({
url: 'https://services.syncfusion.com/react/production/api/GanttData',
adaptor: new WebApiAdaptor,
crossDomain: true
});
return <GanttComponent dataSource={dataSource} taskFields={taskFields} height = '450px'>
</GanttComponent>
};
ReactDOM.render(<App />, document.getElementById('root'));
<!DOCTYPE html>
<html lang="en">
<head>
<title>Syncfusion React Gantt</title>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta name="description" content="Essential JS 2 for React Components" />
<meta name="author" content="Syncfusion" />
<link href="https://cdn.syncfusion.com/ej2/27.2.2/material.css" rel="stylesheet" type="text/css"/>
<link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet" />
<script src="https://cdnjs.cloudflare.com/ajax/libs/systemjs/0.19.38/system.js"></script>
<script src="systemjs.config.js"></script>
<style>
#loader {
color: #008cff;
height: 40px;
left: 45%;
position: absolute;
top: 45%;
width: 30%;
}
</style>
<script src="https://cdn.syncfusion.com/ej2/syncfusion-helper.js" type ="text/javascript"></script>
</head>
<body>
<div id='root'>
<div id='loader'>Loading....</div>
</div>
</body>
</html>
URL Adaptor
In Gantt, we can fetch data from SQL database using ADO.NET
Entity Data Model and update the changes on CRUD action to the server by using DataManager
support. To communicate with the remote data we are using UrlAdaptor
of DataManager property to call the server method and get back resultant data in JSON format. We can know more about UrlAdaptor
from here
.
Please refer the link to create the
ADO.NET
Entity Data Model in Visual Studio,
We can define data source for Gantt as instance of DataManager using url
property of DataManager. Please Check the below code snippet to assign data source to Gantt.
import * as React from 'react';
import * as ReactDOM from 'react-dom';
import { GanttComponent } from '@syncfusion/ej2-react-gantt';
import { DataManager, UrlAdaptor } from '@syncfusion/ej2-data';
class App extends React.Component<{}, {}>{
public taskFields: any = {
id: 'TaskId',
name: 'TaskName',
startDate: 'StartDate',
duration: 'Duration',
dependency: 'Predecessor',
child: 'SubTasks'
};
public dataSource: DataManager = new DataManager({
url: '/Home/UrlDatasource',
adaptor: new UrlAdaptor
});
render() {
return <GanttComponent dataSource={this.dataSource} taskFields={this.taskFields} height = '450px'>
</GanttComponent>
}
};
ReactDOM.render(<App />, document.getElementById('root'));
GanttDataSourceEntities db = new GanttDataSourceEntities();
public ActionResult UrlDatasource(DataManagerRequest dm)
{
List<GanttData>DataList = db.GanttDatas.ToList();
var count = DataList.Count();
return Json(new { result = DataList, count = count });
}
Remote Save Adaptor
You may need to perform all Gantt Actions on the client-side except the CRUD operations, which should be interacted with the server-side to persist data. It can be achieved in Gantt by using RemoteSaveAdaptor.
Datasource must be set to the json property and set RemoteSaveAdaptor to the adaptor property. CRUD operations can be mapped to the server-side using the batchUrl properties.
You can use the following code example to use RemoteSaveAdaptor in Gantt.
import * as React from 'react';
import * as ReactDOM from 'react-dom';
import { GanttComponent } from '@syncfusion/ej2-react-gantt';
import { DataManager, RemoteSaveAdaptor } from '@syncfusion/ej2-data';
class App extends React.Component<{}, {}>{
public taskFields: any = {
id: 'taskId',
name: 'taskName',
startDate: 'startDate',
duration: 'duration',
progress: 'progress',
parentID: 'parentID',
dependency: 'predecessor',
},
toolbar: ['Add', 'Edit', 'Update', 'Delete', 'Cancel', 'ExpandAll', 'CollapseAll', 'Indent', 'Outdent'],
editSettings: {
allowAdding: true,
allowEditing: true,
allowDeleting: true,
allowTaskbarEditing: true,
showDeleteConfirmDialog: true
},
public dataSource: DataManager = new DataManager({
batchUrl: '/Home/BatchUpdate',
adaptor: new RemoteSaveAdaptor
});
render() {
return <GanttComponent dataSource={this.dataSource} taskFields={this.taskFields} height = '450px'>
</GanttComponent>
}
};
ReactDOM.render(<App />, document.getElementById('root'));
The following code example describes the CRUD operations handled at server-side.
public IActionResult BatchUpdate([FromBody] CRUDModel batchmodel)
{
public class CRUDModel
{
public List<GanttDataSource> added { get; set; }
public List<GanttDataSource> changed { get; set; }
public List<GanttDataSource> deleted { get; set; }
public object key { get; set; }
public string action { get; set; }
public string table { get; set; }
}
public IActionResult BatchUpdate([FromBody] CRUDModel batchmodel)
{
if (batchmodel.changed != null)
{
for (var i = 0; i < batchmodel.changed.Count(); i++)
{
var value = batchmodel.changed[i];
GanttDataSource result = DataList.Where(or => or.taskId == value.taskId).FirstOrDefault();
result.taskId = value.taskId;
result.taskName = value.taskName;
result.startDate = value.startDate;
result.endDate = value.endDate;
result.duration = value.duration;
result.progress = value.progress;
result.parentID = value.parentID;
}
}
if (batchmodel.deleted != null)
{
for (var i = 0; i < batchmodel.deleted.Count(); i++)
{
DataList.Remove(DataList.Where(or => or.taskId.Equals(batchmodel.deleted[i].taskId)).FirstOrDefault());
RemoveChildRecords(batchmodel.deleted[i].taskId);
}
}
if (batchmodel.added != null)
{
for (var i = 0; i < batchmodel.added.Count(); i++)
{
DataList.Add(batchmodel.added[i]);
}
}
return Json(new { addedRecords = batchmodel.added, changedRecords = batchmodel.changed, deletedRecords = batchmodel.deleted });
}
public void RemoveChildRecords(int key)
{
var childList = DataList.Where(x => x.parentID == key).ToList();
foreach (var item in childList)
{
DataList.Remove(item);
RemoveChildRecords(item.taskId);
}
}
return Json(new { addedRecords = batchmodel.added, changedRecords = batchmodel.changed, deletedRecords = batchmodel.deleted });
}
Load child on demand
To render child records on demand, assign a remote service URL in the instance of DataManager to the Url property. To interact with the remote data source, provide the endpoint URL and also define the hasChildMapping
property in taskFields of Gantt Chart.
The hasChildMapping
property maps the field name in the data source, which denotes whether the current record holds any child records. This is useful internally to show expand icon while binding child data on demand.
When loadChildOnDemand
is disabled, all the root nodes are rendered in a collapsed state at initial load. On expanding the root node, the child nodes will be loaded from the remote server.
When enableVirtualization
is enabled and loadChildOnDemand
is disabled, only the current viewport root nodes are rendered in a collapsed state.
When a root node is expanded, its child nodes are rendered and maintained in a collection locally, such that on consecutive expand/collapse actions on the root node, the child nodes are loaded locally instead of from the remote server.
When the loadChildOnDemand
is enabled, parent records are rendered in an expanded state.
import { DataManager, WebApiAdaptor } from '@syncfusion/ej2-data';
import { Selection, VirtualScroll } from '@syncfusion/ej2-react-gantt';
import * as React from 'react';
function App() {
const dataSource: DataManager = new DataManager({
url: 'https://services.syncfusion.com/react/production/api/GanttLoadOnDemand',
adaptor: new WebApiAdaptor,
crossDomain: true
});
const taskFields: any = {
id: 'taskId',
name: 'taskName',
startDate: 'startDate',
endDate: 'endDate',
duration: 'duration',
progress: 'progress',
hasChildMapping: 'isParent',
parentID: 'parentID',
expandState:'IsExpanded'
};
const projectStartDate: Date = new Date('01/02/2000');
const projectEndDate: Date = new Date('12/01/2002');
return <GanttComponent id='LoadOnDemand' dataSource={dataSource} treeColumnIndex={1}
taskFields={taskFields} enableVirtualization={true} loadChildOnDemand={false} height='460px'
projectStartDate={projectStartDate} projectEndDate={projectEndDate}>
<ColumnsDirective>
<ColumnDirective field='taskId' width='80' ></ColumnDirective>
<ColumnDirective field='taskName' headerText='Job Name' width='250' clipMode='EllipsisWithTooltip'></ColumnDirective>
<ColumnDirective field='startDate'></ColumnDirective>
<ColumnDirective field='duration'></ColumnDirective>
<ColumnDirective field='progress'></ColumnDirective>
</ColumnsDirective>
<Inject services={[Selection, VirtualScroll]} />
</GanttComponent>
};
export default App;
The following code example describes handling of Load on demand at server end.
public object Get()
{
DataOperations operation = new DataOperations();
var queryString = Request.Query;
if (tree.Count == 0)
tree = TreeData.GetTree();
if (queryString.Keys.Contains("$filter") && !queryString.Keys.Contains("$top"))
{
StringValues filter;
queryString.TryGetValue("$filter", out filter);
int? fltr;
if (filter[0].ToString().Split("eq")[1] == " null")
{
fltr = null;
}
else
{
fltr = Int32.Parse(filter[0].ToString().Split("eq")[1]);
}
IQueryable<TreeData> data1 = tree.Where(f => f.parentID == fltr).AsQueryable();
return new { result = data1.ToList(), count = data1.Count() };
}
StringValues expand;
queryString.TryGetValue("$expand", out expand);
if (queryString.Keys.Contains("$expand")) // setting the ExpandStateMapping property whether is true or false
{
if (expand[0].ToString().Split(",")[0] == "ExpandingAction")
{
var val = TreeData.GetTree().Where(ds => ds.taskId == int.Parse(expand[0].ToString().Split(",")[1])).FirstOrDefault();
val.IsExpanded = true;
}
else if (expand[0].ToString().Split(",")[0] == "CollapsingAction")
{
var val = TreeData.GetTree().Where(ds => ds.taskId == int.Parse(expand[0].ToString().Split(",")[1])).FirstOrDefault();
val.IsExpanded = false;
}
}
List<TreeData> data = tree.ToList();
if (queryString.Keys.Contains("$select"))
{
data = (from ord in tree
select new TreeData
{
parentID = ord.parentID
}
).ToList();
return data;
}
data = data.Where(p => p.parentID == null).ToList();
int count = data.Count;
if (queryString.Keys.Contains("$inlinecount"))
{
StringValues Skip;
StringValues Take;
StringValues loadchild;
int skip = (queryString.TryGetValue("$skip", out Skip)) ? Convert.ToInt32(Skip[0]) : 0;
int top = (queryString.TryGetValue("$top", out Take)) ? Convert.ToInt32(Take[0]) : data.Count();
var GroupData = TreeData.GetTree().ToList().GroupBy(rec => rec.parentID)
.Where(g => g.Key != null).ToDictionary(g => g.Key?.ToString(), g => g.ToList());
foreach (var Record in data.ToList())
{
if (GroupData.ContainsKey(Record.taskId.ToString()))
{
var ChildGroup = GroupData[Record.taskId.ToString()];
if (ChildGroup?.Count > 0)
AppendChildren(ChildGroup, Record, GroupData, data);
}
}
if (expand.Count > 0 && expand[0].ToString().Split(",")[0] == "CollapsingAction")
{
string IdMapping = "taskId";
List<WhereFilter> CollapseFilter = new List<WhereFilter>();
CollapseFilter.Add(new WhereFilter() { Field = IdMapping, value = expand[0].ToString().Split(",")[1], Operator = "equal" });
var CollapsedParentRecord = operation.PerformFiltering(data, CollapseFilter, "and");
var index = data.Cast<object>().ToList().IndexOf(CollapsedParentRecord.Cast<object>().ToList()[0]);
skip = index;
}
else if (expand.Count > 0 && expand[0].ToString().Split(",")[0] == "ExpandingAction")
{
string IdMapping = "taskId";
List<WhereFilter> ExpandFilter = new List<WhereFilter>();
ExpandFilter.Add(new WhereFilter() { Field = IdMapping, value = expand[0].ToString().Split(",")[1], Operator = "equal" });
var ExpandedParentRecord = operation.PerformFiltering(data, ExpandFilter, "and");
var index = data.Cast<object>().ToList().IndexOf(ExpandedParentRecord.Cast<object>().ToList()[0]);
skip = index;
}
return new { result = data.Skip(skip).Take(top), count = data.Count };
}
else
{
return TreeData.GetTree();
}
}
private void AppendChildren(List<TreeData> ChildRecords, TreeData ParentItem, Dictionary<string, List<TreeData>> GroupData, List<TreeData> data)
{
var queryString = Request.Query;
string TaskId = ParentItem.taskId.ToString();
var index = data.IndexOf(ParentItem);
foreach (var Child in ChildRecords)
{
string ParentId = Child.parentID.ToString();
if (TaskId == ParentId && (bool)ParentItem.IsExpanded)
{
if (data.IndexOf(Child) == -1)
((IList)data).Insert(++index, Child);
if (GroupData.ContainsKey(Child.taskId.ToString()))
{
var DeepChildRecords = GroupData[Child.taskId.ToString()];
if (DeepChildRecords?.Count > 0)
AppendChildren(DeepChildRecords, Child, GroupData, data);
}
}
}
}
// GET: api/Orders/
[HttpGet("{id}", Name = "Get")]
public string Get(int id)
{
return "value";
}
[HttpPost]
public object Post([FromBody] TreeData[] value)
{
//handle insert action
for (var i = 0; i < value.Length; i++)
{
tree.Insert(0, value[i]);
}
return value;
}
//// PUT: api/Orders
[HttpPut]
public object Put([FromBody] TreeData[] value)
{
//handle edit action
if (value.Length == 1 && value[0].isParent == true)
{
UpdateDependentRecords(value[0]);
}
for (var i = 0; i < value.Length; i++) {
var ord = value[i];
TreeData val = tree.Where(or => or.taskId == ord.taskId).FirstOrDefault();
val.taskId = ord.taskId;
val.taskName = ord.taskName;
val.endDate = ord.endDate;
val.startDate = ord.startDate;
val.duration = ord.duration;
val.predecessor = ord.predecessor;
}
return value;
}
private void UpdateDependentRecords(TreeData ParentItem)
{
var data = tree.Where(p => p.parentID == ParentItem.taskId).ToList();
var previousData = tree.Where(p => p.taskId == ParentItem.taskId).ToList();
var previousStartDate = previousData[0].startDate;
var previousEndDate = previousData[0].endDate;
double sdiff = (double)GetTimeDifference((DateTime)previousStartDate, (DateTime)ParentItem.startDate);
double ediff = (double)GetTimeDifference((DateTime)previousEndDate, (DateTime)ParentItem.endDate);
GetRootChildRecords(ParentItem);
for(var i=0; i<ChildRecords.Count;i++)
{
ChildRecords[i].startDate = ((DateTime)ChildRecords[i].startDate).AddSeconds(sdiff);
ChildRecords[i].endDate = ((DateTime)ChildRecords[i].endDate).AddSeconds(ediff);
}
}
private void GetRootChildRecords(TreeData ParentItem)
{
var currentchildRecords = tree.Where(p => p.parentID == ParentItem.taskId).ToList();
for (var i = 0; i < currentchildRecords.Count; i++) {
var currentRecord = currentchildRecords[i];
ChildRecords.Add(currentRecord);
if (currentRecord.isParent == true)
{
GetRootChildRecords(currentRecord);
}
}
}
public object GetTimeDifference(DateTime sdate, DateTime edate)
{
return new DateTime(edate.Year, edate.Month, edate.Day, edate.Hour, edate.Minute, edate.Second, DateTimeKind.Utc).Subtract(new DateTime(sdate.Year, sdate.Month, sdate.Day, sdate.Hour, sdate.Minute, sdate.Second, DateTimeKind.Utc)).TotalSeconds;
}
// DELETE: api/ApiWithActions
[HttpDelete("{id:int}")]
[Route("Orders/{id:int}")]
public object Delete(int id)
{
//handle delete action
tree.Remove(tree.Where(or => or.taskId == id).FirstOrDefault());
return Json(id);
}
public class CRUDModel<T> where T : class
{
public TreeData Value;
public int Key { get; set; }
public int RelationalKey { get; set; }
public List<TreeData> added { get; set; }
public List<TreeData> changed { get; set; }
public List<TreeData> deleted { get; set; }
}
public class TreeData
{
public static List<TreeData> tree = new List<TreeData>();
[System.ComponentModel.DataAnnotations.Key]
public int taskId { get; set; }
public string taskName { get; set; }
public DateTime startDate { get; set; }
public DateTime endDate { get; set; }
public string duration { get; set; }
public int progress { get; set; }
public int? parentID { get; set; }
public string predecessor { get; set; }
public bool? isParent { get; set; }
public bool? IsExpanded { get; set; }
public static List<TreeData> GetTree()
{
if (tree.Count == 0)
{
Random rand = new Random();
var x = 0;
int duration = 0;
DateTime startDate = new DateTime(2000, 1, 3, 08, 00, 00);
for (var i = 1; i <= 50; i++)
{
startDate = startDate.AddDays(i == 1 ? 0 : 7);
DateTime childStartDate = startDate;
TreeData Parent = new TreeData()
{
taskId = ++x,
taskName = "Task " + x,
startDate = startDate,
endDate = childStartDate.AddDays(26),
duration = "20",
progress = rand.Next(100),
predecessor = null,
isParent = true,
parentID = null,
IsExpanded = false
};
tree.Add(Parent);
for (var j = 1; j <= 4; j++)
{
childStartDate = childStartDate.AddDays(j == 1 ? 0 : duration + 2);
duration = 5;
tree.Add(new TreeData()
{
taskId = ++x,
taskName = "Task " + x,
startDate = childStartDate,
endDate = childStartDate.AddDays(5),
duration = duration.ToString(),
progress = rand.Next(100),
parentID = Parent.taskId,
predecessor = (j > 1 ? (x - 1) + "FS" : ""),
isParent = false,
IsExpanded = false
});
}
}
}
return tree;
}
}
Limitations
- Filtering, sorting and searching are not supported in load on demand.
- Only Self-Referential type data is supported with remote data binding in Gantt Chart.
- Load-on-demand supports only the validated data source.
Sending additional parameters to the server
We can pass additional parameters using addParams
method of Query
class. In server side we have inherited and shown the additional parameter value in Syncfusion DataManager class itself. We pass an additional parameter in load time using load
event. We can also pass additional parameter to the CRUD model. Please Check the below code snippet to send additional parameter to Gantt.
import * as React from 'react';
import * as ReactDOM from 'react-dom';
import { GanttComponent, Inject, Edit, Toolbar } from '@syncfusion/ej2-react-gantt';
import { DataManager, UrlAdaptor, Query } from '@syncfusion/ej2-data';
class App extends React.Component<{}, {}>{
public taskFields: any = {
id: 'TaskId',
name: 'TaskName',
startDate: 'StartDate',
duration: 'Duration',
dependency: 'Predecessor',
parentID: 'parentID',
};
this.editOptions = {
allowAdding: true,
allowEditing: true,
allowDeleting: true
};
this.toolbarOptions = ['Add', 'Edit', 'Delete', 'Cancel', 'Update', 'ExpandAll', 'CollapseAll'];
public dataSource: DataManager = new DataManager({
url: 'http://localhost:50039/Home/UrlDatasource',
adaptor: new UrlAdaptor,
batchUrl: 'http://localhost:50039/Home/BatchSave',
});
load(args) {
this.ganttInstance.query = new Query().addParams('ej2Gantt', "test");
}
render() {
return <GanttComponent dataSource={this.dataSource} taskFields={this.taskFields} editSettings={this.editOptions} toolbar={this.toolbarOptions} load={this.load.bind(this)} height = '450px' ref={gantt => this.ganttInstance = gantt}>
<Inject services={[Edit, Toolbar]}/>
</GanttComponent>
}
};
ReactDOM.render(<App />, document.getElementById('root'));
namespace URLAdaptor.Controllers
{
public class HomeController : Controller
{
...///
//inherit the class to show age as property of DataManager
public class Test : DataManagerRequest
{
public string ej2Gantt { get; set; }
}
public ActionResult UrlDatasource([FromBody]Test dm)
{
if (DataList == null)
{
ProjectData datasource = new ProjectData();
DataList = datasource.GetUrlDataSource();
}
var count = DataList.Count();
return Json(new { result = DataList, count = count }, JsonRequestBehavior.AllowGet);
}
...///
public class ICRUDModel<T> where T : class
{
public object key { get; set; }
public T value { get; set; }
public List<T> added { get; set; }
public List<T> changed { get; set; }
public List<T> deleted { get; set; }
public IDictionary<string, object> @params { get; set; }
}
...///
}
}
You can find the full sample from here.
Handling HTTP error
During server interaction from the Gantt, some server-side exceptions may occur, and you can acquire those error messages or exception details in client-side using the actionFailure
event.
The argument passed to the actionFailure
event contains the error details returned from the server.
import * as React from 'react';
import * as ReactDOM from 'react-dom';
import { GanttComponent } from '@syncfusion/ej2-react-gantt';
import { DataManager } from '@syncfusion/ej2-data';
function App(){
const taskFields = {
id: 'TaskId',
name: 'TaskName',
startDate: 'StartDate',
duration: 'Duration',
dependency: 'Predecessor',
parentID: 'parentID',
};
const dataSource = new DataManager({
url: 'http://some.com/invalidUrl'
});
let ganttInstance;
function actionFailure(args) {
let span = document.createElement('span');
ganttInstance.element.parentNode.insertBefore(span, ganttInstance.element);
span.style.color = '#FF0000'
span.innerHTML = 'Server exception: 404 Not found';
}
return <GanttComponent dataSource={dataSource} taskFields={taskFields} actionFailure={actionFailure} height = '450px'
ref={gantt => ganttInstance = gantt}>
</GanttComponent>
};
ReactDOM.render(<App />, document.getElementById('root'));
import * as React from 'react';
import * as ReactDOM from 'react-dom';
import { GanttComponent } from '@syncfusion/ej2-react-gantt';
import { DataManager } from '@syncfusion/ej2-data';
function App(){
const taskFields: any = {
id: 'TaskId',
name: 'TaskName',
startDate: 'StartDate',
duration: 'Duration',
dependency: 'Predecessor',
parentID: 'parentID',
};
const dataSource: DataManager = new DataManager({
url: 'http://some.com/invalidUrl'
});
let ganttInstance;
function actionFailure(args) {
let span: HTMLElement = document.createElement('span');
ganttInstance.element.parentNode.insertBefore(span, ganttInstance.element);
span.style.color = '#FF0000'
span.innerHTML = 'Server exception: 404 Not found';
}
return <GanttComponent dataSource={dataSource} taskFields={taskFields} actionFailure={actionFailure} height = '450px'
ref={gantt => ganttInstance = gantt}>
</GanttComponent>
};
ReactDOM.render(<App />, document.getElementById('root'));
<!DOCTYPE html>
<html lang="en">
<head>
<title>Syncfusion React Gantt</title>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta name="description" content="Essential JS 2 for React Components" />
<meta name="author" content="Syncfusion" />
<link href="https://cdn.syncfusion.com/ej2/27.2.2/material.css" rel="stylesheet" type="text/css"/>
<link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet" />
<script src="https://cdnjs.cloudflare.com/ajax/libs/systemjs/0.19.38/system.js"></script>
<script src="systemjs.config.js"></script>
<style>
#loader {
color: #008cff;
height: 40px;
left: 45%;
position: absolute;
top: 45%;
width: 30%;
}
</style>
<script src="https://cdn.syncfusion.com/ej2/syncfusion-helper.js" type ="text/javascript"></script>
</head>
<body>
<div id='root'>
<div id='loader'>Loading....</div>
</div>
</body>
</html>
Binding with Ajax
You can use Gantt dataSource
property to bind the data source to Gantt from external Ajax request. In the below code we have fetched the data source from the server with the help of Ajax request and provided that to dataSource
property by using onSuccess
event of the Ajax.
import * as React from 'react';
import * as ReactDOM from 'react-dom';
import { GanttComponent } from '@syncfusion/ej2-react-gantt';
import { ButtonComponent } from '@syncfusion/ej2-react-buttons';
import { Ajax } from '@syncfusion/ej2-base';
function App (){
const taskFields= {
id: 'TaskId',
name: 'TaskName',
startDate: 'StartDate',
duration: 'Duration',
dependency: 'Predecessor',
parentID: 'parentID',
};
let ganttInstance;
function clickHandler() {
let ajax = new Ajax("https://services.syncfusion.com/react/production/api/GanttData","GET");
ganttInstance.showSpinner();
ajax.send();
ajax.onSuccess = function (data) {
ganttInstance.hideSpinner();
ganttInstance.dataSource = (JSON.parse(data)).Items;
ganttInstance.refresh();
};
}
return <div>
<ButtonComponent onClick={clickHandler}>Bind Data</ButtonComponent>
<GanttComponent taskFields={taskFields} projectStartDate='02/24/2019' projectEndDate='07/20/2019' height = '450px' ref={gantt => ganttInstance = gantt}>
</GanttComponent>
</div>
};
ReactDOM.render(<App />, document.getElementById('root'));;
import * as React from 'react';
import * as ReactDOM from 'react-dom';
import { GanttComponent } from '@syncfusion/ej2-react-gantt';
import { ButtonComponent } from '@syncfusion/ej2-react-buttons';
import { Ajax } from '@syncfusion/ej2-base';
function App (){
const taskFields: any = {
id: 'TaskId',
name: 'TaskName',
startDate: 'StartDate',
duration: 'Duration',
dependency: 'Predecessor',
parentID: 'parentID',
};
let ganttInstance:any;
function clickHandler() {
let ajax = new Ajax("https://services.syncfusion.com/react/production/api/GanttData","GET");
ganttInstance.showSpinner();
ajax.send();
ajax.onSuccess = function (data: string) {
ganttInstance.hideSpinner();
ganttInstance.dataSource = (JSON.parse(data)).Items;
ganttInstance.refresh();
};
}
return <div>
<ButtonComponent onClick={clickHandler}>Bind Data</ButtonComponent>
<GanttComponent taskFields={taskFields} projectStartDate='02/24/2019' projectEndDate='07/20/2019' height = '450px' ref={gantt => ganttInstance = gantt}>
</GanttComponent>
</div>
};
ReactDOM.render(<App />, document.getElementById('root'));;
<!DOCTYPE html>
<html lang="en">
<head>
<title>Syncfusion React Gantt</title>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta name="description" content="Essential JS 2 for React Components" />
<meta name="author" content="Syncfusion" />
<link href="https://cdn.syncfusion.com/ej2/27.2.2/material.css" rel="stylesheet" type="text/css"/>
<link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet" />
<script src="https://cdnjs.cloudflare.com/ajax/libs/systemjs/0.19.38/system.js"></script>
<script src="systemjs.config.js"></script>
<style>
#loader {
color: #008cff;
height: 40px;
left: 45%;
position: absolute;
top: 45%;
width: 30%;
}
</style>
<script src="https://cdn.syncfusion.com/ej2/syncfusion-helper.js" type ="text/javascript"></script>
</head>
<body>
<div id='root'>
<div id='loader'>Loading....</div>
</div>
</body>
</html>
Note: If you bind the dataSource from this way, then it acts like a local dataSource. So you cannot perform any server side crud actions.
Split task
The Split-task
feature allows you to split a task or interrupt the work during planned or unforeseen circumstances.
We can split the task either in load time or dynamically, by defining the segments either in hierarchical or self-referential way.
Hierarchical
To split a task at load time in hierarchical way, we need to define the segment details in datasource and this field should be mapped by using the taskFields.segments
property.
[
{
TaskID: 1, TaskName: 'Identify Site location', StartDate: new Date('04/02/2019'), Duration: 4, Progress: 50,
Segments: [
{ StartDate: new Date("04/02/2019"), Duration: 2 },
{ StartDate: new Date("04/04/2019"), Duration: 2 }
]
}
]
let GanttData = [
{
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: 4, Progress: 50,
Segments: [
{ StartDate: new Date("04/02/2019"), Duration: 2 },
{ StartDate: new Date("04/04/2019"), Duration: 2 }
] },
{ 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 , 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: 3, Progress: 50 }
]
}
];
import * as React from 'react';
import * as ReactDOM from 'react-dom';
import { GanttComponent } from '@syncfusion/ej2-react-gantt';
function App () {
const taskFields = {
id: 'TaskID',
name: 'TaskName',
startDate: 'StartDate',
endDate: 'EndDate',
duration: 'Duration',
progress: 'Progress',
child: 'subtasks',
segments: 'Segments'
};
return <GanttComponent dataSource={GanttData} taskFields={taskFields} height='450px'>
</GanttComponent>;
};
ReactDOM.render(<App />, document.getElementById('root'));
let GanttData = [
{
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: 4, Progress: 50,
Segments: [
{ StartDate: new Date("04/02/2019"), Duration: 2 },
{ StartDate: new Date("04/04/2019"), Duration: 2 }
] },
{ 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 , 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: 3, Progress: 50 }
]
}
];
import * as React from 'react';
import * as ReactDOM from 'react-dom';
import { GanttComponent } from '@syncfusion/ej2-react-gantt';
function App () {
const taskFields = {
id: 'TaskID',
name: 'TaskName',
startDate: 'StartDate',
endDate: 'EndDate',
duration: 'Duration',
progress: 'Progress',
child: 'subtasks',
segments: 'Segments'
};
return <GanttComponent dataSource={GanttData} taskFields={taskFields} height='450px'>
</GanttComponent>;
};
ReactDOM.render(<App />, document.getElementById('root'));
<!DOCTYPE html>
<html lang="en">
<head>
<title>Syncfusion React Gantt</title>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta name="description" content="Essential JS 2 for React Components" />
<meta name="author" content="Syncfusion" />
<link href="https://cdn.syncfusion.com/ej2/27.2.2/material.css" rel="stylesheet" type="text/css"/>
<link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet" />
<script src="https://cdnjs.cloudflare.com/ajax/libs/systemjs/0.19.38/system.js"></script>
<script src="systemjs.config.js"></script>
<style>
#loader {
color: #008cff;
height: 40px;
left: 45%;
position: absolute;
top: 45%;
width: 30%;
}
</style>
<script src="https://cdn.syncfusion.com/ej2/syncfusion-helper.js" type ="text/javascript"></script>
</head>
<body>
<div id='root'>
<div id='loader'>Loading....</div>
</div>
</body>
</html>
Self-referential
We can also define segment details as a flat data and this collection can be mapped by using segmentData
property. The segment id field of this collection is mapped by using the taskFields.segmentId
property.
taskFields: {
segmentId: "segmentId"
},
segmentData: [
{ segmentId: 1, StartDate: new Date("02/04/2019"), Duration: 2 },
{ segmentId: 1, StartDate: new Date("02/05/2019"), Duration: 5 },
{ segmentId: 4, StartDate: new Date("04/02/2019"), Duration: 2 },
{ segmentId: 4, StartDate: new Date("04/04/2019"), Duration: 2 }
],
let GanttData = [
{
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: 4, Progress: 50,
Segments: [
{ StartDate: new Date("04/02/2019"), Duration: 2 },
{ StartDate: new Date("04/04/2019"), Duration: 2 }
] },
{ 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 , 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: 3, Progress: 50 }
]
}
];
import * as React from 'react';
import * as ReactDOM from 'react-dom';
import { GanttComponent } from '@syncfusion/ej2-react-gantt';
function App() {
const taskFields = {
id: 'TaskID',
name: 'TaskName',
startDate: 'StartDate',
endDate: 'EndDate',
duration: 'Duration',
progress: 'Progress',
child: 'subtasks',
segmentId: 'segmentId'
};
const segmentData = [
{ segmentId: 2, StartDate: new Date("04/02/2019"), Duration: 2 },
{ segmentId: 2, StartDate: new Date("04/04/2019"), Duration: 2 },
{ segmentId: 4, StartDate: new Date("04/02/2019"), Duration: 2 },
{ segmentId: 4, StartDate: new Date("04/04/2019"), Duration: 2 }
];
return (<GanttComponent dataSource={GanttData} taskFields={taskFields} segmentData={segmentData} height='450px'>
</GanttComponent>)
};
ReactDOM.render(<App />, document.getElementById('root'));
let GanttData = [
{
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: 4, Progress: 50,
Segments: [
{ StartDate: new Date("04/02/2019"), Duration: 2 },
{ StartDate: new Date("04/04/2019"), Duration: 2 }
] },
{ 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 , 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: 3, Progress: 50 }
]
}
];
import * as React from 'react';
import * as ReactDOM from 'react-dom';
import { GanttComponent } from '@syncfusion/ej2-react-gantt';
function App() {
const taskFields = {
id: 'TaskID',
name: 'TaskName',
startDate: 'StartDate',
endDate: 'EndDate',
duration: 'Duration',
progress: 'Progress',
child: 'subtasks',
segmentId: 'segmentId'
};
const segmentData = [
{ segmentId: 2, StartDate: new Date("04/02/2019"), Duration: 2 },
{ segmentId: 2, StartDate: new Date("04/04/2019"), Duration: 2 },
{ segmentId: 4, StartDate: new Date("04/02/2019"), Duration: 2 },
{ segmentId: 4, StartDate: new Date("04/04/2019"), Duration: 2 }
];
return (<GanttComponent dataSource={GanttData} taskFields={taskFields} segmentData={segmentData} height='450px'>
</GanttComponent>)
};
ReactDOM.render(<App />, document.getElementById('root'));
<!DOCTYPE html>
<html lang="en">
<head>
<title>Syncfusion React Gantt</title>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta name="description" content="Essential JS 2 for React Components" />
<meta name="author" content="Syncfusion" />
<link href="https://cdn.syncfusion.com/ej2/27.2.2/material.css" rel="stylesheet" type="text/css"/>
<link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet" />
<script src="https://cdnjs.cloudflare.com/ajax/libs/systemjs/0.19.38/system.js"></script>
<script src="systemjs.config.js"></script>
<style>
#loader {
color: #008cff;
height: 40px;
left: 45%;
position: absolute;
top: 45%;
width: 30%;
}
</style>
<script src="https://cdn.syncfusion.com/ej2/syncfusion-helper.js" type ="text/javascript"></script>
</head>
<body>
<div id='root'>
<div id='loader'>Loading....</div>
</div>
</body>
</html>
Note: Segment id field contains id of a task which should be split at load time.
Improve performance by disabling validations
The autoCalculateDateScheduling
property can help you reduce the time taken for the Gantt chart to render on the initial load. When this API is enabled, parent-child validation, data validation, and predecessor validation are restricted, allowing the Gantt chart to load more quickly. Since we are disabling the validations, data source provided to gantt should have all data such as start date, end date, duration, as proper data.
let tempData = [
{
TaskID: 1, TaskName: 'Product concept',StartDate: new Date('04/02/2019'), EndDate: new Date('04/21/2019'),
parentID: 0
},
{
TaskID: 2, TaskName: 'Defining the product and its usage', StartDate: new Date('04/02/2019'),
Duration: 3, Progress: 30, parentID: 1
},
{
TaskID: 3, TaskName: 'Defining target audience', StartDate: new Date('04/02/2019'),
parentID: 1, Duration: 3
},
{
TaskID: 4, TaskName: 'Prepare product sketch and notes', StartDate: new Date('04/05/2019'),
Duration: 2, parentID: 1, Progress: 30
},
{
TaskID: 5, TaskName: 'Concept approval', StartDate: new Date('04/08/2019'),
parentID: 0, Duration: 0
},
{
TaskID: 6, TaskName: 'Market research', StartDate: new Date('04/02/2019'),
parentID: 0, EndDate: new Date('04/21/2019')
},
{
TaskID: 7, TaskName: 'Demand analysis', StartDate: new Date('04/04/2019'),
EndDate: new Date('04/21/2019'), parentID: 6
},
{
TaskID: 8, TaskName: 'Customer strength', StartDate: new Date('04/09/2019'),
Duration: 4, parentID: 7, Progress: 30
},
{
TaskID: 9, TaskName: 'Market opportunity analysis', StartDate: new Date('04/09/2019'),
Duration: 4, parentID: 7
},
{
TaskID: 10, TaskName: 'Competitor analysis', StartDate: new Date('04/15/2019'),
Duration: 4, parentID: 6, Progress: 30
},
{
TaskID: 11, TaskName: 'Product strength analsysis', StartDate: new Date('04/15/2019'),
Duration: 4, parentID: 6
},
{
TaskID: 12, TaskName: 'Research complete', StartDate: new Date('04/18/2019'),
Duration: 0, parentID: 6
},
{
TaskID: 13, TaskName: 'Product design and development', StartDate: new Date('04/04/2019'),
parentID: 0, EndDate: new Date('04/21/2019')
},
{
TaskID: 14, TaskName: 'Functionality design', StartDate: new Date('04/19/2019'),
Duration: 3, parentID: 13, Progress: 30
},
{
TaskID: 15, TaskName: 'Quality design', StartDate: new Date('04/19/2019'),
Duration: 3, parentID: 13
},
{
TaskID: 16, TaskName: 'Define reliability', StartDate: new Date('04/24/2019'),
Duration: 2, Progress: 30, parentID: 13
},
{
TaskID: 17, TaskName: 'Identifying raw materials', StartDate: new Date('04/24/2019'),
Duration: 2, parentID: 13
},
{
TaskID: 18, TaskName: 'Define cost plan', StartDate: new Date('04/04/2019'),
parentID: 13, EndDate: new Date('04/21/2019')
},
{
TaskID: 19, TaskName: 'Manufacturing cost', StartDate: new Date('04/26/2019'),
Duration: 2, Progress: 30, parentID: 18
},
{
TaskID: 20, TaskName: 'Selling cost', StartDate: new Date('04/26/2019'),
Duration: 2, parentID: 18
},
{
TaskID: 21, TaskName: 'Development of the final design', StartDate: new Date('04/30/2019'),
parentID: 13, EndDate: new Date('04/21/2019')
},
{
TaskID: 22, TaskName: 'Defining dimensions and package volume', StartDate: new Date('04/30/2019'),
Duration: 2, parentID: 21, Progress: 30
},
{
TaskID: 23, TaskName: 'Develop design to meet industry standards', StartDate: new Date('05/02/2019'),
Duration: 2, parentID: 21
},
{
TaskID: 24, TaskName: 'Include all the details', StartDate: new Date('05/06/2019'),
Duration: 3, parentID: 21
},
{
TaskID: 25, TaskName: 'CAD computer-aided design', StartDate: new Date('05/09/2019'),
Duration: 3, parentID: 13, Progress: 30
},
{
TaskID: 26, TaskName: 'CAM computer-aided manufacturing', StartDate: new Date('09/14/2019'),
Duration: 3, parentID: 13
},
{
TaskID: 27, TaskName: 'Design complete', StartDate: new Date('05/16/2019'),
Duration: 0, parentID: 13
},
{
TaskID: 28, TaskName: 'Prototype testing', StartDate: new Date('05/17/2019'),
Duration: 4, Progress: 30, parentID: 0
},
{
TaskID: 29, TaskName: 'Include feedback', StartDate: new Date('05/17/2019'),
Duration: 4, parentID: 0
},
{
TaskID: 30, TaskName: 'Manufacturing', StartDate: new Date('05/23/2019'),
Duration: 5, Progress: 30, parentID: 0
},
{
TaskID: 31, TaskName: 'Assembling materials to finsihed goods', StartDate: new Date('05/30/2019'),
Duration: 5, parentID: 0
},
{
TaskID: 32, TaskName: 'Feedback and testing', StartDate: new Date('04/04/2019'),
parentID: 0, EndDate: new Date('04/21/2019'),
},
{
TaskID: 33, TaskName: 'Internal testing and feedback', StartDate: new Date('06/06/2019'),
Duration: 3, parentID: 32, Progress: 45
},
{
TaskID: 34, TaskName: 'Customer testing and feedback', StartDate: new Date('06/11/2019'),
Duration: 3, parentID: 32, Progress: 50
},
{
TaskID: 35, TaskName: 'Final product development', StartDate: new Date('04/04/2019'),
parentID: 0, EndDate: new Date('04/21/2019'),
},
{
TaskID: 36, TaskName: 'Important improvements', StartDate: new Date('06/14/2019'),
Duration: 4, Progress: 30, parentID: 35
},
{
TaskID: 37, TaskName: 'Address any unforeseen issues', StartDate: new Date('06/14/2019'),
Duration: 4, Progress: 30, parentID: 35
},
{
TaskID: 38, TaskName: 'Final product', StartDate: new Date('04/04/2019'),
parentID: 0, EndDate: new Date('04/21/2019'),
},
{
TaskID: 39, TaskName: 'Branding product', StartDate: new Date('06/20/2019'),
Duration: 4, parentID: 38
},
{
TaskID: 40, TaskName: 'Marketing and presales', StartDate: new Date('06/26/2019'), Duration: 4,
Progress: 30, parentID: 38
}
];
let virtualData = [];
let projId = 1;
for (let i = 0; i < 50; i++) {
let x = virtualData.length + 1;
let parent = {};
/* tslint:disable:no-string-literal */
parent['TaskID'] = x;
parent['TaskName'] = 'Project ' + (i + 1);
virtualData.push(parent);
for (let j = 0; j < tempData.length; j++) {
let subtasks = {};
/* tslint:disable:no-string-literal */
subtasks['TaskID'] = tempData[j].TaskID + x;
subtasks['TaskName'] = tempData[j].TaskName;
subtasks['StartDate'] = tempData[j].StartDate;
subtasks['Duration'] = tempData[j].Duration;
subtasks['Progress'] = tempData[j].Progress;
subtasks['parentID'] = tempData[j].parentID + x;
virtualData.push(subtasks);
}
}
import * as React from 'react';
import * as ReactDOM from 'react-dom';
import { GanttComponent,ColumnsDirective,ColumnDirective,Inject,Edit, Selection, Toolbar, VirtualScroll } from '@syncfusion/ej2-react-gantt';
function App (){
const taskFields = {
id: 'TaskID',
name: 'TaskName',
startDate: 'StartDate',
endDate: 'EndDate',
duration: 'Duration',
progress: 'Progress',
parentID: 'parentID'
};
const splitterSettings= {
columnIndex: 2
};
const labelSettings = {
taskLabel: 'Progress'
};
const editSettings = {
allowAdding: true,
allowEditing: true,
allowDeleting: true,
allowTaskbarEditing: true,
showDeleteConfirmDialog: true
};
const toolbar = ['Add', 'Edit', 'Update', 'Delete', 'Cancel', 'ExpandAll', 'CollapseAll', 'Indent', 'Outdent'];
return (
<GanttComponent dataSource={virtualData} treeColumnIndex={1} labelSettings={labelSettings}
allowSelection={true} highlightWeekends={true} enableVirtualization={true}
taskFields={taskFields} splitterSettings={splitterSettings} autoCalculateDateScheduling={false} toolbar={toolbar} editSettings={editSettings} height='450px'>
<ColumnsDirective>
<ColumnDirective field='TaskID'/>
<ColumnDirective field='TaskName' headerText='Task Name'/>
<ColumnDirective field='StartDate'/>
<ColumnDirective field='Duration'/>
<ColumnDirective field='Progress'/>
</ColumnsDirective>
<Inject services={[Selection, VirtualScroll,Edit,Toolbar]} />
</GanttComponent>
)
};
ReactDOM.render(<App />, document.getElementById('root'));
let tempData: any[] = [
{
TaskID: 1, TaskName: 'Product concept',StartDate: new Date('04/02/2019'), EndDate: new Date('04/21/2019'),
parentID: 0
},
{
TaskID: 2, TaskName: 'Defining the product and its usage', StartDate: new Date('04/02/2019'),
Duration: 3, Progress: 30, parentID: 1
},
{
TaskID: 3, TaskName: 'Defining target audience', StartDate: new Date('04/02/2019'),
parentID: 1, Duration: 3
},
{
TaskID: 4, TaskName: 'Prepare product sketch and notes', StartDate: new Date('04/05/2019'),
Duration: 2, parentID: 1, Progress: 30
},
{
TaskID: 5, TaskName: 'Concept approval', StartDate: new Date('04/08/2019'),
parentID: 0, Duration: 0
},
{
TaskID: 6, TaskName: 'Market research', StartDate: new Date('04/02/2019'),
parentID: 0, EndDate: new Date('04/21/2019')
},
{
TaskID: 7, TaskName: 'Demand analysis', StartDate: new Date('04/04/2019'),
EndDate: new Date('04/21/2019'), parentID: 6
},
{
TaskID: 8, TaskName: 'Customer strength', StartDate: new Date('04/09/2019'),
Duration: 4, parentID: 7, Progress: 30
},
{
TaskID: 9, TaskName: 'Market opportunity analysis', StartDate: new Date('04/09/2019'),
Duration: 4, parentID: 7
},
{
TaskID: 10, TaskName: 'Competitor analysis', StartDate: new Date('04/15/2019'),
Duration: 4, parentID: 6, Progress: 30
},
{
TaskID: 11, TaskName: 'Product strength analsysis', StartDate: new Date('04/15/2019'),
Duration: 4, parentID: 6
},
{
TaskID: 12, TaskName: 'Research complete', StartDate: new Date('04/18/2019'),
Duration: 0, parentID: 6
},
{
TaskID: 13, TaskName: 'Product design and development', StartDate: new Date('04/04/2019'),
parentID: 0, EndDate: new Date('04/21/2019')
},
{
TaskID: 14, TaskName: 'Functionality design', StartDate: new Date('04/19/2019'),
Duration: 3, parentID: 13, Progress: 30
},
{
TaskID: 15, TaskName: 'Quality design', StartDate: new Date('04/19/2019'),
Duration: 3, parentID: 13
},
{
TaskID: 16, TaskName: 'Define reliability', StartDate: new Date('04/24/2019'),
Duration: 2, Progress: 30, parentID: 13
},
{
TaskID: 17, TaskName: 'Identifying raw materials', StartDate: new Date('04/24/2019'),
Duration: 2, parentID: 13
},
{
TaskID: 18, TaskName: 'Define cost plan', StartDate: new Date('04/04/2019'),
parentID: 13, EndDate: new Date('04/21/2019')
},
{
TaskID: 19, TaskName: 'Manufacturing cost', StartDate: new Date('04/26/2019'),
Duration: 2, Progress: 30, parentID: 18
},
{
TaskID: 20, TaskName: 'Selling cost', StartDate: new Date('04/26/2019'),
Duration: 2, parentID: 18
},
{
TaskID: 21, TaskName: 'Development of the final design', StartDate: new Date('04/30/2019'),
parentID: 13, EndDate: new Date('04/21/2019')
},
{
TaskID: 22, TaskName: 'Defining dimensions and package volume', StartDate: new Date('04/30/2019'),
Duration: 2, parentID: 21, Progress: 30
},
{
TaskID: 23, TaskName: 'Develop design to meet industry standards', StartDate: new Date('05/02/2019'),
Duration: 2, parentID: 21
},
{
TaskID: 24, TaskName: 'Include all the details', StartDate: new Date('05/06/2019'),
Duration: 3, parentID: 21
},
{
TaskID: 25, TaskName: 'CAD computer-aided design', StartDate: new Date('05/09/2019'),
Duration: 3, parentID: 13, Progress: 30
},
{
TaskID: 26, TaskName: 'CAM computer-aided manufacturing', StartDate: new Date('09/14/2019'),
Duration: 3, parentID: 13
},
{
TaskID: 27, TaskName: 'Design complete', StartDate: new Date('05/16/2019'),
Duration: 0, parentID: 13
},
{
TaskID: 28, TaskName: 'Prototype testing', StartDate: new Date('05/17/2019'),
Duration: 4, Progress: 30, parentID: 0
},
{
TaskID: 29, TaskName: 'Include feedback', StartDate: new Date('05/17/2019'),
Duration: 4, parentID: 0
},
{
TaskID: 30, TaskName: 'Manufacturing', StartDate: new Date('05/23/2019'),
Duration: 5, Progress: 30, parentID: 0
},
{
TaskID: 31, TaskName: 'Assembling materials to finsihed goods', StartDate: new Date('05/30/2019'),
Duration: 5, parentID: 0
},
{
TaskID: 32, TaskName: 'Feedback and testing', StartDate: new Date('04/04/2019'),
parentID: 0, EndDate: new Date('04/21/2019'),
},
{
TaskID: 33, TaskName: 'Internal testing and feedback', StartDate: new Date('06/06/2019'),
Duration: 3, parentID: 32, Progress: 45
},
{
TaskID: 34, TaskName: 'Customer testing and feedback', StartDate: new Date('06/11/2019'),
Duration: 3, parentID: 32, Progress: 50
},
{
TaskID: 35, TaskName: 'Final product development', StartDate: new Date('04/04/2019'),
parentID: 0, EndDate: new Date('04/21/2019'),
},
{
TaskID: 36, TaskName: 'Important improvements', StartDate: new Date('06/14/2019'),
Duration: 4, Progress: 30, parentID: 35
},
{
TaskID: 37, TaskName: 'Address any unforeseen issues', StartDate: new Date('06/14/2019'),
Duration: 4, Progress: 30, parentID: 35
},
{
TaskID: 38, TaskName: 'Final product', StartDate: new Date('04/04/2019'),
parentID: 0, EndDate: new Date('04/21/2019'),
},
{
TaskID: 39, TaskName: 'Branding product', StartDate: new Date('06/20/2019'),
Duration: 4, parentID: 38
},
{
TaskID: 40, TaskName: 'Marketing and presales', StartDate: new Date('06/26/2019'), Duration: 4,
Progress: 30, parentID: 38
}
];
let virtualData: any[] = [];
let projId: number = 1;
for (let i: number = 0; i < 50; i++) {
let x: number = virtualData.length + 1;
let parent: any = {};
/* tslint:disable:no-string-literal */
parent['TaskID'] = x;
parent['TaskName'] = 'Project ' + (i + 1);
virtualData.push(parent);
for (let j: number = 0; j < tempData.length; j++) {
let subtasks: any = {};
/* tslint:disable:no-string-literal */
subtasks['TaskID'] = tempData[j].TaskID + x;
subtasks['TaskName'] = tempData[j].TaskName;
subtasks['StartDate'] = tempData[j].StartDate;
subtasks['Duration'] = tempData[j].Duration;
subtasks['Progress'] = tempData[j].Progress;
subtasks['parentID'] = tempData[j].parentID + x;
virtualData.push(subtasks);
}
}
import * as React from 'react';
import * as ReactDOM from 'react-dom';
import { GanttComponent,ColumnsDirective,ColumnDirective,Inject,Edit, Selection, Toolbar, VirtualScroll } from '@syncfusion/ej2-react-gantt';
function App (){
const taskFields: any = {
id: 'TaskID',
name: 'TaskName',
startDate: 'StartDate',
endDate: 'EndDate',
duration: 'Duration',
progress: 'Progress',
parentID: 'parentID'
};
const splitterSettings: any = {
columnIndex: 2
};
const labelSettings: any = {
taskLabel: 'Progress'
};
const editSettings: any = {
allowAdding: true,
allowEditing: true,
allowDeleting: true,
allowTaskbarEditing: true,
showDeleteConfirmDialog: true
};
const toolbar: any = ['Add', 'Edit', 'Update', 'Delete', 'Cancel', 'ExpandAll', 'CollapseAll', 'Indent', 'Outdent'];
return (
<GanttComponent dataSource={virtualData} treeColumnIndex={1} labelSettings={labelSettings}
allowSelection={true} highlightWeekends={true} enableVirtualization={true}
taskFields={taskFields} splitterSettings={splitterSettings} autoCalculateDateScheduling={false} toolbar={toolbar} editSettings={editSettings} height='450px'>
<ColumnsDirective>
<ColumnDirective field='TaskID'/>
<ColumnDirective field='TaskName' headerText='Task Name'/>
<ColumnDirective field='StartDate'/>
<ColumnDirective field='Duration'/>
<ColumnDirective field='Progress'/>
</ColumnsDirective>
<Inject services={[Selection, VirtualScroll,Edit,Toolbar]} />
</GanttComponent>
)
};
ReactDOM.render(<App />, document.getElementById('root'));
<!DOCTYPE html>
<html lang="en">
<head>
<title>Syncfusion React Gantt</title>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta name="description" content="Essential JS 2 for React Components" />
<meta name="author" content="Syncfusion" />
<link href="https://cdn.syncfusion.com/ej2/27.2.2/material.css" rel="stylesheet" type="text/css"/>
<link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet" />
<script src="https://cdnjs.cloudflare.com/ajax/libs/systemjs/0.19.38/system.js"></script>
<script src="systemjs.config.js"></script>
<style>
#loader {
color: #008cff;
height: 40px;
left: 45%;
position: absolute;
top: 45%;
width: 30%;
}
</style>
<script src="https://cdn.syncfusion.com/ej2/syncfusion-helper.js" type ="text/javascript"></script>
</head>
<body>
<div id='root'>
<div id='loader'>Loading....</div>
</div>
</body>
</html>
Limitations
Gantt has the support for both Hierarchical and Self-Referential data binding. When rendering the Gantt control with SQL database, we suggest you to use the Self-Referential data binding to maintain the parent-child relation. Because the complex json structure is very difficult to manage it in SQL tables, we need to write a complex queries and we have to write a complex algorithm to find out the proper record details while updating/deleting the inner level task in Gantt data source. We cannot implement both data binding for Gantt control and this is not a recommended way. If both child and parentID are mapped, the records will not render properly because, when task id of a record defined in the hierarchy structure is assigned to parent id of another record, in such case the records will not properly render. As the self-referential will search the record with particular id in flat data only, not in the inner level of records. If we map the parentID field, it will be prioritized and Gantt will be rendered based on the parentID values.