The TreeGrid uses DataManager
, which supports both RESTful JSON data services binding and local JavaScript object array binding. The dataSource
property can be assigned either with the instance of DataManager
or JavaScript object array collection.
It supports two kinds of data binding method:
In Local Data binding, data source for rendering the TreeGrid control is retrieved from the same application locally.
Two types of Data binding are possible with the TreeGrid control.
To bind local data to the treegrid, 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
.
By default,
DataManager
usesJsonAdaptor
for local data-binding.
The childMapping
property is used to map the child records in hierarchy data source.
The following code example shows you how to bind the hierarchical local data into the TreeGrid control.
ej.treegrid.TreeGrid.Inject(ej.treegrid.Page);
var treeGridObj = new ej.treegrid.TreeGrid({
dataSource: sampleData,
childMapping: 'subtasks',
allowPaging: true,
pageSettings: {pageSize: 7},
treeColumnIndex: 1,
columns: [
{ field: 'taskID', headerText: 'Task ID', width: 90, textAlign: 'Right' },
{ field: 'taskName', headerText: 'Task Name', width: 180, textAlign: 'Left' },
{
field: 'startDate', headerText: 'Start Date', width: 90, textAlign: 'Right', type: 'date', format: 'yMd'
},
{ field: 'duration', headerText: 'Duration', width: 80, textAlign: 'Right' }
]
});
treeGridObj.appendTo('#TreeGrid');
<!DOCTYPE html><html lang="en"><head>
<title>EJ2 Grid</title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="description" content="Typescript Grid Control">
<meta name="author" content="Syncfusion">
<link href="index.css" rel="stylesheet">
<link href="//cdn.syncfusion.com/ej2/ej2-base/styles/material.css" rel="stylesheet">
<link href="//cdn.syncfusion.com/ej2/ej2-grids/styles/material.css" rel="stylesheet">
<link href="//cdn.syncfusion.com/ej2/ej2-treegrid/styles/material.css" rel="stylesheet">
<link href="//cdn.syncfusion.com/ej2/ej2-buttons/styles/material.css" rel="stylesheet">
<link href="//cdn.syncfusion.com/ej2/ej2-popups/styles/material.css" rel="stylesheet">
<link href="//cdn.syncfusion.com/ej2/ej2-navigations/styles/material.css" rel="stylesheet">
<link href="//cdn.syncfusion.com/ej2/ej2-dropdowns/styles/material.css" rel="stylesheet">
<link href="//cdn.syncfusion.com/ej2/ej2-lists/styles/material.css" rel="stylesheet">
<link href="//cdn.syncfusion.com/ej2/ej2-inputs/styles/material.css" rel="stylesheet">
<link href="//cdn.syncfusion.com/ej2/ej2-calendars/styles/material.css" rel="stylesheet">
<link href="//cdn.syncfusion.com/ej2/ej2-splitbuttons/styles/material.css" rel="stylesheet">
<script src="https://cdn.syncfusion.com/ej2/dist/ej2.min.js" type="text/javascript"></script>
<script src="es5-datasource.js" type="text/javascript"></script>
</head>
<body>
<div id="container">
<div id="TreeGrid"></div>
</div>
<script>
var ele = document.getElementById('container');
if(ele) {
ele.style.visibility = "visible";
}
</script>
<script src="index.js" type="text/javascript"></script>
</body></html>
TreeGrid is rendered from Self-Referential data structures by providing two fields, ID field and parent ID field.
idMapping
property.parentIdMapping
property.ej.treegrid.TreeGrid.Inject(ej.treegrid.Page);
var treeGridObj = new ej.treegrid.TreeGrid({
dataSource: projectData,
idMapping: 'TaskID',
parentIdMapping: 'parentID',
allowPaging: true,
pageSettings: {pageSize: 7},
treeColumnIndex: 1,
columns: [
{ field: 'TaskID', headerText: 'Task ID', width: 90, textAlign: 'Right' },
{ field: 'TaskName', headerText: 'Task Name', width: 180 },
{
field: 'StartDate', headerText: 'Start Date', width: 90, textAlign: 'Right', type: 'date',format: 'yMd'
},
{ field: 'Duration', headerText: 'Duration', width: 80, textAlign: 'Right' }
]
});
treeGridObj.appendTo('#TreeGrid');
<!DOCTYPE html><html lang="en"><head>
<title>EJ2 Grid</title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="description" content="Typescript Grid Control">
<meta name="author" content="Syncfusion">
<link href="index.css" rel="stylesheet">
<link href="//cdn.syncfusion.com/ej2/ej2-base/styles/material.css" rel="stylesheet">
<link href="//cdn.syncfusion.com/ej2/ej2-grids/styles/material.css" rel="stylesheet">
<link href="//cdn.syncfusion.com/ej2/ej2-treegrid/styles/material.css" rel="stylesheet">
<link href="//cdn.syncfusion.com/ej2/ej2-buttons/styles/material.css" rel="stylesheet">
<link href="//cdn.syncfusion.com/ej2/ej2-popups/styles/material.css" rel="stylesheet">
<link href="//cdn.syncfusion.com/ej2/ej2-navigations/styles/material.css" rel="stylesheet">
<link href="//cdn.syncfusion.com/ej2/ej2-dropdowns/styles/material.css" rel="stylesheet">
<link href="//cdn.syncfusion.com/ej2/ej2-lists/styles/material.css" rel="stylesheet">
<link href="//cdn.syncfusion.com/ej2/ej2-inputs/styles/material.css" rel="stylesheet">
<link href="//cdn.syncfusion.com/ej2/ej2-calendars/styles/material.css" rel="stylesheet">
<link href="//cdn.syncfusion.com/ej2/ej2-splitbuttons/styles/material.css" rel="stylesheet">
<script src="https://cdn.syncfusion.com/ej2/dist/ej2.min.js" type="text/javascript"></script>
<script src="es5-datasource.js" type="text/javascript"></script>
</head>
<body>
<div id="container">
<div id="TreeGrid"></div>
</div>
<script>
var ele = document.getElementById('container');
if(ele) {
ele.style.visibility = "visible";
}
</script>
<script src="index.js" type="text/javascript"></script>
</body></html>
Herewith we have provided list of reserved properties and the purpose we used internally in TreeGrid. We recommend to avoid these reserved properties in your dataSource(To get rid of conflicts).
Reserved keywords | Purpose |
---|---|
childRecords | Specifies the childRecords of a parentData |
hasChildRecords | Specifies whether the record contains child records |
hasFilteredChildRecords | Specifies whether the record contains filtered child records |
expanded | Specifies whether the child records are expanded |
parentItem | Specifies the parentItem of childRecords |
index | Specifies the index of current record |
level | Specifies the hierarchy level of record |
filterLevel | Specifies the hierarchy level of filtered record |
parentIdMapping | Specifies the parentID |
uniqueID | Specifies the unique ID of a record |
parentUniqueID | Specifies the parent Unique ID of a record |
checkboxState | Specifies the checkbox state of a record |
isSummaryRow | Specifies the summary of a record |
taskData | Specifies the main data |
primaryParent | Specifies the Primary data |
To bind remote data to TreeGrid component, assign service data as an instance of DataManager
to the dataSource
property. To interact with remote data source, provide the endpoint url
and define the hasChildMapping
property of treegrid.
The hasChildMapping
property maps the field name in data source, that denotes whether current record holds any child records. This is useful internally to show expand icon while binding child data on demand.
The TreeGrid provides Load on Demand
support for rendering remote data. The Load on demand is considered in TreeGrid for the following actions.
When load on demand is enabled, all the root nodes are rendered in collapsed state at initial load.
When load on demand support is enabled in TreeGrid with paging, the current or active page’s root node alone will be rendered in collapsed state. On expanding the root node, the child nodes will be loaded from the remote server.
When a root node is expanded, its child nodes are rendered and are cached locally, such that on consecutive expand/collapse actions on root node, the child nodes are loaded from the cache instead from the remote server.
Similarly, if the user navigates to a new page, the root nodes of that specific page, will be rendered with request to the remote server.
ej.treegrid.TreeGrid.Inject(ej.treegrid.Page);
var data = new ej.data.DataManager({
url: 'https://ej2services.syncfusion.com/production/web-services/api/SelfReferenceData',
adaptor: new ej.data.WebApiAdaptor(),
crossDomain: true
});
var treeGridObj = new ej.treegrid.TreeGrid({
dataSource: data,
hasChildMapping: 'isParent',
idMapping: 'TaskID',
parentIdMapping: 'ParentItem',
height: 260,
treeColumnIndex: 1,
allowPaging: true,
columns: [
{ field: 'TaskID', headerText: 'Task ID', width: 80, textAlign: 'Right' },
{ field: 'TaskName', headerText: 'Task Name', width: 200, textAlign: 'Left' },
{ field: 'StartDate', headerText: 'Start Date', width: 90, textAlign: 'Right', type: 'date', format: 'yMd' },
{ field: 'Duration', headerText: 'Duration', width: 90, textAlign: 'Right' },
]
});
treeGridObj.appendTo('#TreeGrid');
<!DOCTYPE html><html lang="en"><head>
<title>EJ2 Grid</title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="description" content="Typescript Grid Control">
<meta name="author" content="Syncfusion">
<link href="index.css" rel="stylesheet">
<link href="//cdn.syncfusion.com/ej2/ej2-base/styles/material.css" rel="stylesheet">
<link href="//cdn.syncfusion.com/ej2/ej2-grids/styles/material.css" rel="stylesheet">
<link href="//cdn.syncfusion.com/ej2/ej2-treegrid/styles/material.css" rel="stylesheet">
<link href="//cdn.syncfusion.com/ej2/ej2-buttons/styles/material.css" rel="stylesheet">
<link href="//cdn.syncfusion.com/ej2/ej2-popups/styles/material.css" rel="stylesheet">
<link href="//cdn.syncfusion.com/ej2/ej2-navigations/styles/material.css" rel="stylesheet">
<link href="//cdn.syncfusion.com/ej2/ej2-dropdowns/styles/material.css" rel="stylesheet">
<link href="//cdn.syncfusion.com/ej2/ej2-lists/styles/material.css" rel="stylesheet">
<link href="//cdn.syncfusion.com/ej2/ej2-inputs/styles/material.css" rel="stylesheet">
<link href="//cdn.syncfusion.com/ej2/ej2-calendars/styles/material.css" rel="stylesheet">
<link href="//cdn.syncfusion.com/ej2/ej2-splitbuttons/styles/material.css" rel="stylesheet">
<script src="https://cdn.syncfusion.com/ej2/dist/ej2.min.js" type="text/javascript"></script>
<script src="es5-datasource.js" type="text/javascript"></script>
</head>
<body>
<div id="container">
<div id="TreeGrid"></div>
</div>
<script>
var ele = document.getElementById('container');
if(ele) {
ele.style.visibility = "visible";
}
</script>
<script src="index.js" type="text/javascript"></script>
</body></html>
By default,
DataManager
usesODataAdaptor
for remote data-binding. Based on the RESTful web services, set the corresponding adaptor to DataManager. Referhere
for more details. Filtering and searching server-side data operations are not supported in load on demand
While binding remote data to Tree Grid component, by default Tree Grid renders parent rows in collapsed state. Tree Grid provides option to load the child records also during the initial rendering itself for remote data binding by setting loadChildOnDemand
as true.
When loadChildOnDemand
is enabled parent records are rendered in expanded state.
The following code example describes the behavior of the loadChildOnDemand feature of Tree Grid.
import { TreeGrid, Page }from '@syncfusion/ej2-treegrid';
import { DataManager, UrlAdaptor } from '@syncfusion/ej2-data';
TreeGrid.Inject(Page);
let data: DataManager = new DataManager({
url: "Home/DataSource",
updateUrl: "Home/Update",
insertUrl: "Home/Insert",
removeUrl: "Home/Delete",
batchUrl: "Home/Remove",
adaptor: new UrlAdaptor
});
let treegrid: TreeGrid = new TreeGrid({
dataSource: data,
idMapping: 'TaskID',
parentIdMapping: 'ParentItem',
hasChildMapping: 'isParent',
loadChildOnDemand: true,
height: 260,
allowPaging: true,
treeColumnIndex: 1,
columns: [
{ field: 'TaskID', headerText: 'Task ID', textAlign: 'Right', width: 90 },
{ field: 'TaskName', headerText: 'Task Name', width: 180 },
{ field: 'StartDate', headerText: 'Start Date', textAlign: 'Right', width: 90, format: { skeleton: 'yMd', type: 'date' } },
{ field: 'Duration', headerText: 'Duration', width: 80, textAlign: 'Right' }
]
});
treegrid.appendTo('#TreeGrid');
Also while using loadChildOnDemand we need to handle the child records on server end and it is applicable to CRUD operations also.
The following code example describes handling of child records at server end.
public ActionResult UrlDatasource(DataManagerRequest dm)
{
if (TreeData.tree.Count == 0)
TreeData.GetTree();
IEnumerable DataSource = TreeData.tree;
DataOperations operation = new DataOperations();
if (dm.Where != null && dm.Where.Count > 0)
{
DataSource = operation.PerformFiltering(DataSource, dm.Where, "and"); //perform filtering
}
if (dm.Sorted != null && dm.Sorted.Count > 0)
{
DataSource = operation.PerformSorting(DataSource, dm.Sorted); //perform sorting
}
var count = DataSource.ToList<TreeData>().Count();
if (dm.Skip != 0)
{
DataSource = operation.PerformSkip(DataSource, dm.Skip); //Paging
}
if (dm.Take != 0)
{
DataSource = operation.PerformTake(DataSource, dm.Take);
}
if (dm.Where != null)
{
DataSource = CollectChildRecords(DataSource, dm); // method to collect child records
}
return dm.RequiresCounts ? Json(new { result = DataSource, count = count }) : Json(DataSource);
}
public IEnumerable CollectChildRecords(IEnumerable datasource, DataManagerRequest dm)
{
DataOperations operation = new DataOperations();
IEnumerable DataSource = TreeData.tree; // use the total DataSource here
string IdMapping = "TaskID"; // define your IdMapping field name here
int[] TaskIds = new int[0];
foreach (var rec in datasource)
{
int taskid = (int)rec.GetType().GetProperty(IdMapping).GetValue(rec);
TaskIds = TaskIds.Concat(new int[] { taskid }).ToArray(); //get the Parentrecord Ids based on IdMapping Field
}
IEnumerable ChildRecords = null;
foreach (int id in TaskIds)
{
dm.Where[0].value = id;
IEnumerable records = operation.PerformFiltering(DataSource, dm.Where, dm.Where[0].Operator); //perform filtering to collect the childrecords based on Ids
ChildRecords = ChildRecords == null || (ChildRecords.AsQueryable().Count() == 0) ? records : ((IEnumerable<object>)ChildRecords).Concat((IEnumerable<object>)records); //concate the childrecords with dataSource
}
if (ChildRecords != null)
{
ChildRecords = CollectChildRecords(ChildRecords, dm); // repeat the operation for inner level child
if (dm.Sorted != null && dm.Sorted.Count > 0) // perform Sorting
{
ChildRecords = operation.PerformSorting(ChildRecords, dm.Sorted);
}
datasource = ((IEnumerable<object>)datasource).Concat((IEnumerable<object>)ChildRecords); //concate the childrecords with dataSource
}
return datasource;
}
On remote data binding, all treegrid actions such as paging, loading child on-demand, will be processed on server-side. To avoid postback, set the treegrid to load all data on initialization and make the actions process in client-side. To enable this behavior, use the offline
property of DataManager
.
ej.treegrid.TreeGrid.Inject(ej.treegrid.Page);
var data = new ej.data.DataManager({
url: 'https://ej2services.syncfusion.com/production/web-services/api/SelfReferenceData',
adaptor: new ej.data.WebApiAdaptor(),
crossDomain: true,
offline: true
});
var treeGridObj = new ej.treegrid.TreeGrid({
dataSource: data,
idMapping: 'TaskID',
parentIdMapping: 'ParentItem',
height: 260,
treeColumnIndex: 1,
allowPaging: true,
columns: [
{ field: 'TaskID', headerText: 'Task ID', width: 80, textAlign: 'Right' },
{ field: 'TaskName', headerText: 'Task Name', width: 200, textAlign: 'Left' },
{ field: 'StartDate', headerText: 'Start Date', width: 90, textAlign: 'Right', type: 'date', format: 'yMd' },
{ field: 'Duration', headerText: 'Duration', width: 90, textAlign: 'Right' },
]
});
treeGridObj.appendTo('#TreeGrid');
<!DOCTYPE html><html lang="en"><head>
<title>EJ2 Grid</title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="description" content="Typescript Grid Control">
<meta name="author" content="Syncfusion">
<link href="index.css" rel="stylesheet">
<link href="//cdn.syncfusion.com/ej2/ej2-base/styles/material.css" rel="stylesheet">
<link href="//cdn.syncfusion.com/ej2/ej2-grids/styles/material.css" rel="stylesheet">
<link href="//cdn.syncfusion.com/ej2/ej2-treegrid/styles/material.css" rel="stylesheet">
<link href="//cdn.syncfusion.com/ej2/ej2-buttons/styles/material.css" rel="stylesheet">
<link href="//cdn.syncfusion.com/ej2/ej2-popups/styles/material.css" rel="stylesheet">
<link href="//cdn.syncfusion.com/ej2/ej2-navigations/styles/material.css" rel="stylesheet">
<link href="//cdn.syncfusion.com/ej2/ej2-dropdowns/styles/material.css" rel="stylesheet">
<link href="//cdn.syncfusion.com/ej2/ej2-lists/styles/material.css" rel="stylesheet">
<link href="//cdn.syncfusion.com/ej2/ej2-inputs/styles/material.css" rel="stylesheet">
<link href="//cdn.syncfusion.com/ej2/ej2-calendars/styles/material.css" rel="stylesheet">
<link href="//cdn.syncfusion.com/ej2/ej2-splitbuttons/styles/material.css" rel="stylesheet">
<script src="https://cdn.syncfusion.com/ej2/dist/ej2.min.js" type="text/javascript"></script>
<script src="es5-datasource.js" type="text/javascript"></script>
</head>
<body>
<div id="container">
<div id="TreeGrid"></div>
</div>
<script>
var ele = document.getElementById('container');
if(ele) {
ele.style.visibility = "visible";
}
</script>
<script src="index.js" type="text/javascript"></script>
</body></html>
You can create your own adaptor by extending the built-in adaptors. The following demonstrates custom adaptor approach and how to add a serial number for the records by overriding the built-in response processing using the processResponse
method of the WebApiAdaptor
.
ej.treegrid.TreeGrid.Inject(ej.treegrid.Page);
class SerialNoAdaptor extends ej.data.WebApiAdaptor {
processResponse(){
var i = 0;
// calling base class processResponse function
var original = super.processResponse.apply(this, arguments);
// adding serial number
original.forEach((item) => item['Sno'] = ++i);
return original;
}
}
var data = new ej.data.DataManager({
url: 'https://ej2services.syncfusion.com/production/web-services/api/SelfReferenceData',
adaptor: new SerialNoAdaptor,
crossDomain: true,
offline: true
});
var treeGridObj = new ej.treegrid.TreeGrid({
dataSource: data,
idMapping: 'TaskID',
parentIdMapping: 'ParentItem',
height: 260,
treeColumnIndex: 1,
allowPaging: true,
columns: [
{ field: 'Sno', width: 120, headerText: 'SNO', textAlign: 'Right' },
{ field: 'TaskName', headerText: 'Task Name', width: 200, textAlign: 'Left' },
{ field: 'StartDate', headerText: 'Start Date', width: 90, textAlign: 'Right', type: 'date', format: 'yMd' },
{ field: 'Duration', headerText: 'Duration', width: 90, textAlign: 'Right' },
]
});
treeGridObj.appendTo('#TreeGrid');
<!DOCTYPE html><html lang="en"><head>
<title>EJ2 Grid</title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="description" content="Typescript Grid Control">
<meta name="author" content="Syncfusion">
<link href="index.css" rel="stylesheet">
<link href="//cdn.syncfusion.com/ej2/ej2-base/styles/material.css" rel="stylesheet">
<link href="//cdn.syncfusion.com/ej2/ej2-grids/styles/material.css" rel="stylesheet">
<link href="//cdn.syncfusion.com/ej2/ej2-treegrid/styles/material.css" rel="stylesheet">
<link href="//cdn.syncfusion.com/ej2/ej2-buttons/styles/material.css" rel="stylesheet">
<link href="//cdn.syncfusion.com/ej2/ej2-popups/styles/material.css" rel="stylesheet">
<link href="//cdn.syncfusion.com/ej2/ej2-navigations/styles/material.css" rel="stylesheet">
<link href="//cdn.syncfusion.com/ej2/ej2-dropdowns/styles/material.css" rel="stylesheet">
<link href="//cdn.syncfusion.com/ej2/ej2-lists/styles/material.css" rel="stylesheet">
<link href="//cdn.syncfusion.com/ej2/ej2-inputs/styles/material.css" rel="stylesheet">
<link href="//cdn.syncfusion.com/ej2/ej2-calendars/styles/material.css" rel="stylesheet">
<link href="//cdn.syncfusion.com/ej2/ej2-splitbuttons/styles/material.css" rel="stylesheet">
<script src="https://cdn.syncfusion.com/ej2/dist/ej2.min.js" type="text/javascript"></script>
<script src="es5-datasource.js" type="text/javascript"></script>
</head>
<body>
<div id="container">
<div id="TreeGrid"></div>
</div>
<script>
var ele = document.getElementById('container');
if(ele) {
ele.style.visibility = "visible";
}
</script>
<script src="index.js" type="text/javascript"></script>
</body></html>
To add a custom parameter to the data request, use the addParams
method of Query
class. Assign the Query
object with additional parameters to the treegrid query
property.
ej.treegrid.TreeGrid.Inject(ej.treegrid.Page);
var data = new ej.data.DataManager({
url: 'https://ej2services.syncfusion.com/production/web-services/api/SelfReferenceData',
adaptor: new ej.data.WebApiAdaptor(),
crossDomain: true
});
var treeGridObj = new ej.treegrid.TreeGrid({
dataSource: data,
hasChildMapping: 'isParent',
idMapping: 'TaskID',
parentIdMapping: 'ParentItem',
height: 260,
query: new ej.data.Query().addParams('ej2treegrid', 'true'),
treeColumnIndex: 1,
allowPaging: true,
columns: [
{ field: 'TaskID', headerText: 'Task ID', width: 80, textAlign: 'Right' },
{ field: 'TaskName', headerText: 'Task Name', width: 200, textAlign: 'Left' },
{ field: 'StartDate', headerText: 'Start Date', width: 90, textAlign: 'Right', type: 'date', format: 'yMd' },
{ field: 'Duration', headerText: 'Duration', width: 90, textAlign: 'Right' },
]
});
treeGridObj.appendTo('#TreeGrid');
<!DOCTYPE html><html lang="en"><head>
<title>EJ2 Grid</title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="description" content="Typescript Grid Control">
<meta name="author" content="Syncfusion">
<link href="index.css" rel="stylesheet">
<link href="//cdn.syncfusion.com/ej2/ej2-base/styles/material.css" rel="stylesheet">
<link href="//cdn.syncfusion.com/ej2/ej2-grids/styles/material.css" rel="stylesheet">
<link href="//cdn.syncfusion.com/ej2/ej2-treegrid/styles/material.css" rel="stylesheet">
<link href="//cdn.syncfusion.com/ej2/ej2-buttons/styles/material.css" rel="stylesheet">
<link href="//cdn.syncfusion.com/ej2/ej2-popups/styles/material.css" rel="stylesheet">
<link href="//cdn.syncfusion.com/ej2/ej2-navigations/styles/material.css" rel="stylesheet">
<link href="//cdn.syncfusion.com/ej2/ej2-dropdowns/styles/material.css" rel="stylesheet">
<link href="//cdn.syncfusion.com/ej2/ej2-lists/styles/material.css" rel="stylesheet">
<link href="//cdn.syncfusion.com/ej2/ej2-inputs/styles/material.css" rel="stylesheet">
<link href="//cdn.syncfusion.com/ej2/ej2-calendars/styles/material.css" rel="stylesheet">
<link href="//cdn.syncfusion.com/ej2/ej2-splitbuttons/styles/material.css" rel="stylesheet">
<script src="https://cdn.syncfusion.com/ej2/dist/ej2.min.js" type="text/javascript"></script>
<script src="es5-datasource.js" type="text/javascript"></script>
</head>
<body>
<div id="container">
<div id="TreeGrid"></div>
</div>
<script>
var ele = document.getElementById('container');
if(ele) {
ele.style.visibility = "visible";
}
</script>
<script src="index.js" type="text/javascript"></script>
</body></html>
During server interaction from the treegrid, 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.
var data = new ej.data.DataManager({
url: 'http://some.com/invalidUrl'
});
var treeGridObj = new ej.treegrid.TreeGrid({
dataSource: data,
idMapping: 'TaskID',
parentIdMapping: 'ParentItem',
treeColumnIndex: 1,
actionFailure: (e) => {
var span = document.createElement('span');
treeGridObj.element.parentNode.insertBefore(span, treeGridObj.element);
span.style.color = "#FF0000"
span.innerHTML = "Server exception: 404 Not found";
},
columns: [
{ field: 'TaskID', headerText: 'Task ID', width: 80, textAlign: 'Right' },
{ field: 'TaskName', headerText: 'Task Name', width: 200, textAlign: 'Left' },
{ field: 'StartDate', headerText: 'Start Date', width: 90, textAlign: 'Right', type: 'date', format: 'yMd' },
{ field: 'Duration', headerText: 'Duration', width: 90, textAlign: 'Right' },
]
});
treeGridObj.appendTo('#TreeGrid');
<!DOCTYPE html><html lang="en"><head>
<title>EJ2 Grid</title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="description" content="Typescript Grid Control">
<meta name="author" content="Syncfusion">
<link href="index.css" rel="stylesheet">
<link href="//cdn.syncfusion.com/ej2/ej2-base/styles/material.css" rel="stylesheet">
<link href="//cdn.syncfusion.com/ej2/ej2-grids/styles/material.css" rel="stylesheet">
<link href="//cdn.syncfusion.com/ej2/ej2-treegrid/styles/material.css" rel="stylesheet">
<link href="//cdn.syncfusion.com/ej2/ej2-buttons/styles/material.css" rel="stylesheet">
<link href="//cdn.syncfusion.com/ej2/ej2-popups/styles/material.css" rel="stylesheet">
<link href="//cdn.syncfusion.com/ej2/ej2-navigations/styles/material.css" rel="stylesheet">
<link href="//cdn.syncfusion.com/ej2/ej2-dropdowns/styles/material.css" rel="stylesheet">
<link href="//cdn.syncfusion.com/ej2/ej2-lists/styles/material.css" rel="stylesheet">
<link href="//cdn.syncfusion.com/ej2/ej2-inputs/styles/material.css" rel="stylesheet">
<link href="//cdn.syncfusion.com/ej2/ej2-calendars/styles/material.css" rel="stylesheet">
<link href="//cdn.syncfusion.com/ej2/ej2-splitbuttons/styles/material.css" rel="stylesheet">
<script src="https://cdn.syncfusion.com/ej2/dist/ej2.min.js" type="text/javascript"></script>
<script src="es5-datasource.js" type="text/javascript"></script>
</head>
<body>
<div id="container">
<div id="TreeGrid"></div>
</div>
<script>
var ele = document.getElementById('container');
if(ele) {
ele.style.visibility = "visible";
}
</script>
<script src="index.js" type="text/javascript"></script>
</body></html>
The
actionFailure
event will be triggered not only for the server errors, but also when there is an exception while processing the treegrid actions.
You can use TreeGrid dataSource
property to bind the data source to TreeGrid 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.
ej.treegrid.TreeGrid.Inject(ej.treegrid.Page);
var treeGridObj = new ej.treegrid.TreeGrid({
idMapping: 'TaskID',
parentIdMapping: 'ParentItem',
pageSettings: {pageSize: 7},
allowPaging: true,
treeColumnIndex: 1,
columns: [
{ field: 'TaskID', headerText: 'Task ID', width: 80, textAlign: 'Right' },
{ field: 'TaskName', headerText: 'Task Name', width: 200, textAlign: 'Left' },
{ field: 'StartDate', headerText: 'Start Date', width: 90, textAlign: 'Right', type: 'date', format: 'yMd' },
{ field: 'Duration', headerText: 'Duration', width: 90, textAlign: 'Right' },
]
});
treeGridObj.appendTo('#TreeGrid');
let button = document.createElement('button');
button.textContent = 'Bind Data';
treeGridObj.element.parentNode.insertBefore(button, treeGridObj.element);
button.addEventListener("click", function(e){
let ajax = new ej.base.Ajax("https://ej2services.syncfusion.com/production/web-services/api/SelfReferenceData","GET");
treeGridObj.showSpinner();
ajax.send();
ajax.onSuccess = function (data) {
treeGridObj.hideSpinner();
treeGridObj.dataSource = JSON.parse(data);
};
});
<!DOCTYPE html><html lang="en"><head>
<title>EJ2 Grid</title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="description" content="Typescript Grid Control">
<meta name="author" content="Syncfusion">
<link href="index.css" rel="stylesheet">
<link href="//cdn.syncfusion.com/ej2/ej2-base/styles/material.css" rel="stylesheet">
<link href="//cdn.syncfusion.com/ej2/ej2-grids/styles/material.css" rel="stylesheet">
<link href="//cdn.syncfusion.com/ej2/ej2-treegrid/styles/material.css" rel="stylesheet">
<link href="//cdn.syncfusion.com/ej2/ej2-buttons/styles/material.css" rel="stylesheet">
<link href="//cdn.syncfusion.com/ej2/ej2-popups/styles/material.css" rel="stylesheet">
<link href="//cdn.syncfusion.com/ej2/ej2-navigations/styles/material.css" rel="stylesheet">
<link href="//cdn.syncfusion.com/ej2/ej2-dropdowns/styles/material.css" rel="stylesheet">
<link href="//cdn.syncfusion.com/ej2/ej2-lists/styles/material.css" rel="stylesheet">
<link href="//cdn.syncfusion.com/ej2/ej2-inputs/styles/material.css" rel="stylesheet">
<link href="//cdn.syncfusion.com/ej2/ej2-calendars/styles/material.css" rel="stylesheet">
<link href="//cdn.syncfusion.com/ej2/ej2-splitbuttons/styles/material.css" rel="stylesheet">
<script src="https://cdn.syncfusion.com/ej2/dist/ej2.min.js" type="text/javascript"></script>
<script src="es5-datasource.js" type="text/javascript"></script>
</head>
<body>
<div id="container">
<div id="TreeGrid"></div>
</div>
<script>
var ele = document.getElementById('container');
if(ele) {
ele.style.visibility = "visible";
}
</script>
<script src="index.js" type="text/javascript"></script>
</body></html>
- If you bind the dataSource from this way, then it acts like a local dataSource. So you cannot perform any server side crud actions.
It is possible to handle data processing externally and bind the result to the TreeGrid. This helps you to provide your own custom data logic. TreeGrid expects an object as the result of the custom logic and the emitted value should be an object with properties result and count.
In this context, we are going to use DataManager with WebApi Adaptor for handling remote interaction, you can choose any HTTP client as per your choice.
import { TreeGrid, Page} from '../src';
import { DataManager, WebApiAdaptor, Query } from '@syncfusion/ej2-data';
import { isNullOrUndefined } from '@syncfusion/ej2-base';
TreeGrid.Inject(Page);
let data: DataManager = new DataManager({
url: 'http://localhost:51473/api/Tasks', //// url for fetching the data from server
adaptor: new WebApiAdaptor,
crossDomain: true
});
///// filter query for fetching the root level records only and take value should be equal to the pageSize value of pageSettings
data.executeQuery(new Query().where('ParentId', 'equal', null).take(3).skip(0).requiresCount()).then((e: any) => {
let treegrid: TreeGrid = new TreeGrid({
dataSource: { result: e.result, count: e.count },
hasChildMapping: 'IsParent',
idMapping: 'TaskId',
parentIdMapping: 'ParentId',
allowPaging: true,
pageSettings: { pageSize: 3, pageSizeMode: 'Root' },
dataStateChange: function (state: any) {
if (state.action.requestType === 'paging') {
data.executeQuery(new Query().skip(state.skip).take(state.take).requiresCount()).then(function (e: any) {
this.dataSource = { result: e.result, count: e.count };
});
}
},
treeColumnIndex: 1,
columns: [
{ field: 'TaskId', headerText: 'Task ID', isPrimaryKey: true, textAlign: 'Right', width: 120 },
{ field: 'TaskName', headerText: 'Task Name', width: 150 },
{ field: 'StartDate', headerText: 'Start Date', textAlign: 'Right', width: 120, format: { skeleton: 'yMd', type: 'date' } },
{ field: 'Duration', headerText: 'Duration', width: 110, textAlign: 'Right' },
{ field: 'Progress', headerText: 'Progress', width: 110 },
]
});
treegrid.appendTo('#TreeGrid');
});
We have a limitation for Custom Binding feature of TreeGrid. This feature works only for Self Referential data binding with
pageSizeMode
asRoot
.
Using the custom binding feature you can bind the child data for a parent record as per your custom logic. When a parent record is expanded, dataStateChange
event is triggered in which you can assign your custom data to the childData
property of the dataStateChange
event arguments.
After assigning the child data, childDataBind
method should be called from the
dataStateChange
event arguments to indicate that the data is bound.
In this context, initially we have assigned only the parent records to the treegrid dataSource and fetched the required child records in the
dataStateChange
event.
import { TreeGrid, Page, Toolbar, Sort } from '../src';
import { DataManager, WebApiAdaptor, Query } from '@syncfusion/ej2-data';
import { isNullOrUndefined } from '@syncfusion/ej2-base';
TreeGrid.Inject(Page);
let data: DataManager = new DataManager({
url: 'http://localhost:51473/api/Tasks', //// url for fetching the data from server
adaptor: new WebApiAdaptor,
crossDomain: true
});
/// filter query for fetching only the root level records
data.executeQuery(new Query().where('ParentId', 'equal', null).take(3).skip(0).requiresCount()).then((e: any) => {
let treegrid: TreeGrid = new TreeGrid({
dataSource: { result: e.result, count: e.count },
hasChildMapping: 'IsParent',
idMapping: 'TaskId',
parentIdMapping: 'ParentId',
allowPaging: true,
pageSettings: { pageSize: 3, pageSizeMode: 'Root' },
dataStateChange: function (state: any) {
if (!isNullOrUndefined(state.requestType) && state.requestType === 'expand') {
//// filter query for fetching the child records of the expanded record
data.executeQuery(new Query().where('ParentId', 'equal', state.data.TaskId)).then(function (e: any) {
state.childData = e.result;
state.childDataBind();
});
} else {
data.executeQuery(new Query().skip(state.skip).take(state.take).requiresCount()).then(function (e: any) {
this.dataSource = { result: e.result, count: e.count };
});
}
},
treeColumnIndex: 1,
columns: [
{ field: 'TaskId', headerText: 'Task ID', isPrimaryKey: true, textAlign: 'Right', width: 120 },
{ field: 'TaskName', headerText: 'Task Name', width: 150 },
{ field: 'StartDate', headerText: 'Start Date', textAlign: 'Right', width: 120, format: { skeleton: 'yMd', type: 'date' } },
{ field: 'Duration', headerText: 'Duration', width: 110, textAlign: 'Right' },
{ field: 'Progress', headerText: 'Progress', width: 110 },
]
});
treegrid.appendTo('#TreeGrid');
});
For TreeGrid actions such as paging, sorting, etc dataStateChange event will be invoked. You have to query and resolve data using Ajax in this event based on the state arguments.
import { TreeGrid, Page, Toolbar, Sort } from '../src';
import { DataManager, WebApiAdaptor, Query } from '@syncfusion/ej2-data';
import { isNullOrUndefined } from '@syncfusion/ej2-base';
TreeGrid.Inject(Page);
let data: DataManager = new DataManager({
url: 'http://localhost:51473/api/Tasks', //// url for fetching the data from server
adaptor: new WebApiAdaptor,
crossDomain: true
});
/// filter query for fetching only the root level records
data.executeQuery(new Query().where('ParentId', 'equal', null).take(3).skip(0).requiresCount()).then((e: any) => {
let treegrid: TreeGrid = new TreeGrid({
dataSource: { result: e.result, count: e.count },
hasChildMapping: 'IsParent',
idMapping: 'TaskId',
parentIdMapping: 'ParentId',
allowPaging: true,
allowSorting: true,
pageSettings: { pageSize: 3, pageSizeMode: 'Root' },
dataStateChange: function (state: any) {
if (!isNullOrUndefined(state.requestType) && state.requestType === 'expand') {
//// filter query for fetching the child records of the expanded record
data.executeQuery(new Query().where('ParentId', 'equal', state.data.TaskId)).then(function (e: any) {
state.childData = e.result;
state.childDataBind();
});
}
if (state.action.requestType === 'paging') {
data.executeQuery(new Query().skip(state.skip).take(state.take).requiresCount()).then(function (e: any) {
this.dataSource = { result: e.result, count: e.count };
});
}
if (state.action.requestType === 'sorting') {
//// sort query for getting the sorted records from server
data.executeQuery(new Query().sortBy('TaskId', 'descending').addParams('IdMapping','TaskId').skip(state.skip).take(state.take).requiresCount()).then(function (e: any) {
this.dataSource = { result: e.result, count: e.count };
});
}
},
treeColumnIndex: 1,
columns: [
{ field: 'TaskId', headerText: 'Task ID', isPrimaryKey: true, textAlign: 'Right', width: 120 },
{ field: 'TaskName', headerText: 'Task Name', width: 150 },
{ field: 'StartDate', headerText: 'Start Date', textAlign: 'Right', width: 120, format: { skeleton: 'yMd', type: 'date' } },
{ field: 'Duration', headerText: 'Duration', width: 110, textAlign: 'Right' },
{ field: 'Progress', headerText: 'Progress', width: 110 },
]
});
treegrid.appendTo('#TreeGrid');
});
The dataSourceChanged
event will be triggered for updating the treegrid data. You can perform the save operation based on the event arguments and call the endEdit method to indicate the completion of save operation.
import { TreeGrid, Page, Toolbar, Sort, Filter, Edit } from '../src';
import { DataManager, WebApiAdaptor, Query } from '@syncfusion/ej2-data';
import { isNullOrUndefined } from '@syncfusion/ej2-base';
TreeGrid.Inject(Page, Toolbar, Sort, Filter, Edit);
let data: DataManager = new DataManager({
url: 'http://localhost:51473/api/Tasks',
adaptor: new WebApiAdaptor,
crossDomain: true
});
data.executeQuery(new Query().where('ParentId', 'equal', null).take(3).skip(0).requiresCount()).then((e: any) => {
let treegrid: TreeGrid = new TreeGrid({
dataSource: { result: e.result, count: e.count },
hasChildMapping: 'IsParent',
idMapping: 'TaskId',
parentIdMapping: 'ParentId',
allowPaging: true,
toolbar: ['Add', 'Edit', 'Delete', 'Update', 'Cancel'],
editSettings: { allowEditing: true, allowAdding: true, allowDeleting: true, mode: 'Row' },
pageSettings: { pageSize: 3, pageSizeMode: 'Root' },
dataSourceChanged: function (state: any) {
if (state.action == 'add') {
state.endEdit();
}
else if (state.action == 'edit') {
state.endEdit();
}
else if (state.requestType == 'delete') {
state.endEdit();
}
},
dataStateChange: function (state: any) {
if (!isNullOrUndefined(state.requestType) && state.requestType === 'expand') {
data.executeQuery(new Query().where('ParentId', 'equal', state.data.TaskId)).then(function (e: any) {
state.childData = e.result;
state.childDataBind();
});
} else {
data.executeQuery(new Query().where('ParentId', 'equal', null).take(3).skip(0).requiresCount()).then(function (e: any) {
this.dataSource = { result: e.result, count: e.count };
});
}
},
treeColumnIndex: 1,
columns: [
{ field: 'TaskId', headerText: 'Task ID', isPrimaryKey: true, textAlign: 'Right', width: 120 },
{ field: 'TaskName', headerText: 'Task Name', width: 150 },
{ field: 'StartDate', headerText: 'Start Date', textAlign: 'Right', width: 120, format: { skeleton: 'yMd', type: 'date' } },
{ field: 'Duration', headerText: 'Duration', width: 110, textAlign: 'Right' },
{ field: 'Progress', headerText: 'Progress', width: 110 },
]
});
treegrid.appendTo('#TreeGrid');
});
The footer aggregate values should be calculated and sent along with the dataSource property as follows. The aggregate property of the data source should contain the aggregate value assigned to the property named in the field – type format. For example, the Sum aggregate value for the Duration field should be assigned to the property named as Duration - sum.
{
result: [{..}, {..}, {..}, ...],
count: 830,
aggregates: { 'Freight - sum' : 450,'EmployeeID - min': 1 }
}
The dataStateChange
event will be triggered with appropriate arguments when the excel filter requests the filter choice data source. You need to resolve the excel filter data source using the dataSource resolver function from the state argument as follows.
import { TreeGrid, Page, Filter} from '../src';
import { DataManager, WebApiAdaptor, Query } from '@syncfusion/ej2-data';
import { isNullOrUndefined } from '@syncfusion/ej2-base';
TreeGrid.Inject(Page, Filter);
let data: DataManager = new DataManager({
url: 'http://localhost:51473/api/Tasks', //// url for fetching the data from server
adaptor: new WebApiAdaptor,
crossDomain: true
});
///// filter query for fetching the root level records only
data.executeQuery(new Query().where('ParentId', 'equal', null).take(3).skip(0).requiresCount()).then((e: any) => {
let treegrid: TreeGrid = new TreeGrid({
dataSource: { result: e.result, count: e.count },
hasChildMapping: 'IsParent',
idMapping: 'TaskId',
parentIdMapping: 'ParentId',
allowPaging: true,
allowFiltering: true,
filterSettings: { type: 'Excel' },
pageSettings: { pageSize: 3, pageSizeMode: 'Root' },
dataStateChange: function (state: any) {
if (state.action && (state.action.requestType === 'filterchoicerequest'
|| state.action.requestType ==='filtersearchbegin')) {
data.executeQuery(new Query().skip(state.skip).take(state.take).requiresCount()).then(function (e: any) {
state.dataSource && state.dataSource(e); /// resolve the excel filter dataSource
});
}
else {
/// take value must be always be equal to the pageSize value of pageSetings
data.executeQuery(new Query().skip(state.skip).take(state.take).requiresCount()).then(function (e: any) {
this.dataSource = { result: e.result, count: e.count };
});
}
},
treeColumnIndex: 1,
columns: [
{ field: 'TaskId', headerText: 'Task ID', isPrimaryKey: true, textAlign: 'Right', width: 120 },
{ field: 'TaskName', headerText: 'Task Name', width: 150 },
{ field: 'StartDate', headerText: 'Start Date', textAlign: 'Right', width: 120, format: { skeleton: 'yMd', type: 'date' } },
{ field: 'Duration', headerText: 'Duration', width: 110, textAlign: 'Right' },
{ field: 'Progress', headerText: 'Progress', width: 110 },
]
});
treegrid.appendTo('#TreeGrid');
});