
Syncfusion Angular Treegrid
- 214 installs
- Updated August 4, 2026
- syncfusion/angular-ui-components-skills
Use syncfusion-angular-treegrid for development tasks
About
syncfusion-angular-treegrid: A skill for development. This provides functionality for development workflows.
- syncfusion-angular-treegrid
Syncfusion Angular Treegrid by the numbers
- 214 all-time installs (skills.sh)
- +11 installs in the week ending Aug 5, 2026 (Skillselion tracking)
- Ranked #1,879 of 4,347 Backend & APIs skills by installs in the Skillselion catalog
- Data as of Aug 5, 2026 (Skillselion catalog sync)
npx skills add https://github.com/syncfusion/angular-ui-components-skills --skill syncfusion-angular-treegridAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 214 |
|---|---|
| Last updated | August 4, 2026 |
| Repository | syncfusion/angular-ui-components-skills ↗ |
What it does
Use syncfusion-angular-treegrid for development tasks
Files
Syncfusion Angular TreeGrid
Complete guide to implementing and customizing the Syncfusion Angular TreeGrid component for hierarchical data visualization.
⚠️ Security & Trust Boundary
- The TreeGrid skill does not perform any remote data access.
- All external API interaction is handled by a separate DataManager skill outside this skill’s trust boundary.
Table of Contents
- When to Use This Skill
- Data Structure Rules
- Documentation Navigation Guide
- Quick Start Example
- Common Patterns
- Best Practices
- Key Props Summary
- Module Injection
When to Use This Skill
Use this skill when you need to:
- Display hierarchical or tree-structured data in a grid format
- Implement advanced data manipulation (sorting, filtering, searching, editing)
- Configure pagination, virtual scrolling for large datasets
- Add export functionality (PDF, Excel)
- Customize appearance with themes and CSS
- Handle selection, aggregates, and state management
- Support mobile/responsive design
- Implement row/column freezing
Data Structure Rules
Rule 1: childMapping is MANDATORY for Hierarchical Data
Severity: 🔴 CRITICAL - Grid will not expand/collapse without this
Requirement:
// ✅ REQUIRED - Must match data property name exactly
<ejs-treegrid
[dataSource]='data'
childMapping='subtasks'> // Property name is case-sensitive
</ejs-treegrid>
// ❌ WRONG - Will not work
<ejs-treegrid
[dataSource]='data'>
<!-- No expansion possible without childMapping -->
</ejs-treegrid>Data Format:
// ✅ CORRECT - childMapping matches 'subtasks' property
public data = [
{
TaskID: 1,
TaskName: 'Parent',
subtasks: [ // Must match childMapping value
{ TaskID: 2, TaskName: 'Child' }
]
}
];Exception: Use idMapping + parentIdMapping for flat parent-child structure:
// Alternative: Flat structure with parent IDs
<ejs-treegrid
[dataSource]='flatData'
idMapping='TaskID'
parentIdMapping='ParentID'
hasChildMapping='isParent'>
</ejs-treegrid>Rule 2: Data Type Matching is MANDATORY
Severity: 🟠 IMPORTANT - Type mismatches cause rendering/sorting issues
Requirement:
// ✅ CORRECT - Type matches column definition
public data = [
{
TaskID: 1, // number type
TaskName: 'Planning', // string type
StartDate: new Date(), // Date object for date columns
}
];
// Column definition must match data types
<e-columns>
<e-column field='TaskID' headerText='ID' type='number'></e-column>
<e-column field='TaskName' headerText='Task' type='text'></e-column>
<e-column field='StartDate' headerText='Date' type='date' format='yMd'></e-column>
</e-columns>
// ❌ WRONG - Type mismatch
public data = [
{
TaskID: '1', // String instead of number
StartDate: '02/03/2024' // String instead of Date object
}
];Documentation Navigation Guide
Getting Started & Setup
📄 Read: references/getting-started-guide.md
- Complete installation and setup
- Module configuration
- Project initialization
- First TreeGrid component
API Quick Reference
📄 Read: references/properties.md
- 70+ properties in organized tables
- Data binding, display, behavior, features
- Settings objects and modules
- Column properties quick reference
📄 Read: references/methods.md
- 60+ methods in organized tables
- Data management, selection, expand/collapse
- Filtering, sorting, export, editing
- DOM access and utility methods
📄 Read: references/events.md
- 56+ events in organized tables
- Cancelable events and async patterns
- Data, action, selection, edit events
- Export, print, and query events
Recommended for developers who need quick property/method/event lookup
Data Binding
📄 Read: references/data-binding.md
- Local and remote data binding
- ORM and custom adaptor implementation
- Loading data from various sources
Adaptors for Remote Data
📄 Read: references/adaptors.md
- 7 adaptor types for backend integration
- UrlAdaptor, ODataV4Adaptor, WebApiAdaptor, GraphQLAdaptor
- Custom adaptors and RemoteSaveAdaptor
- Backend configuration examples (C#, Node.js)
- Request/response format specifications
- Error handling and adaptor comparison
Observables Data
📄 Read: references/observables.md
- RxJS observables integration
- Async data binding patterns
- Observable-based event handling
- Async operators and pipelines
Column Configuration
📄 Read: references/column.md
- Column definition and width management
- Custom column templates and rendering
- Column reordering and freezing
Column Spanning
📄 Read: references/column-spanning.md
- Multi-column spanning configuration
- Cell spanning across columns
- Dynamic span calculation
Command Columns
📄 Read: references/command-column.md
- Command column with action buttons
- Edit, Delete, and Cancel buttons
- Custom command implementations
Column Menu
📄 Read: references/column-menu.md
- Column header context menu
- Sort, group, and filter menu items
- Custom column menu options
Column Chooser
📄 Read: references/column-chooser.md
- Column visibility toggle UI
- Show/hide columns dynamically
- Column chooser dialog configuration
Row Configuration
📄 Read: references/row.md
- Row templates and detail rows
- Row drag-drop functionality
- Row spanning and customization
Cell-Level Features
📄 Read: references/cell.md
- Cell styling and formatting
- Cell templates and tooltips
- Custom CSS and HTML content
Module System & Architecture
📄 Read: references/modules.md
- Module imports and dependencies
- Required service providers
- Angular version compatibility
- Library setup and initialization
Editing
📄 Read: references/editing.md
- Cell, inline, dialog, row, and batch editing
- Data validation and error handling
- Custom editors and validation rules
Sorting
📄 Read: references/sorting.md
- Single and multi-column sorting
- Custom sort comparers
- Sort order and keyboard navigation
Filtering
📄 Read: references/filtering.md
- Filter bar and menu modes
- Excel-like filtering
- Custom filters and predicates
Searching
📄 Read: references/searching.md
- Global and column-level search
- Case-sensitive searching
- Text highlighting
Selection
📄 Read: references/selection.md
- Row, cell, and checkbox selection modes
- Selection events and APIs
- Programmatic selection
Paging
📄 Read: references/paging.md
- Pagination configuration
- Server-side paging
- Page navigation APIs
Scrolling
📄 Read: references/scrolling.md
- Virtual scrolling for large datasets
- Infinite scroll and lazy loading
- Height/width management and scroll positioning
Frozen Columns
📄 Read: references/frozen-columns.md
- Freeze rows and columns
- Fixed visibility configuration
- Performance optimization
Aggregations
📄 Read: references/aggregates.md
- Summary calculations (Sum, Avg, Min, Max, Count)
- Footer templates and child aggregates
- Aggregate events
Export & Print
📄 Read: references/print.md
- Print functionality and modes
- Page setup and column visibility
- Custom print templates
📄 Read: references/pdf-export.md
- PDF export with styling
- Headers, footers, and watermarks
- Server-side export
📄 Read: references/excel-export.md
- Excel export configuration
- Cell styling and formatting
- Merged cells and custom data
📄 Read: references/csv-export.md
- CSV export configuration
- Custom data formatting for export
- Dynamic file naming
- Export events and callbacks
- Selected records export
User Interface & Interaction
📄 Read: references/toolbar.md
- Built-in toolbar items
- Custom toolbar items and alignment
- Toolbar event handling
📄 Read: references/clipboard.md
- Copy/paste operations
- Custom clipboard formats
- Clipboard event handling
📄 Read: references/context-menu.md
- Context menu configuration
- Built-in and custom menu items
- Menu event handling
Appearance & Responsiveness
📄 Read: references/adaptive.md
- Responsive and adaptive design
- Mobile optimization
- Breakpoints and device detection
📄 Read: references/styling.md
- Built-in themes (Material, Bootstrap, Fabric, Tailwind)
- CSS customization and overrides
- Row/cell conditional styling
Performance
📄 Read: references/performance-optimization.md
- Virtual scrolling and lazy loading
- Pagination for large datasets
- Column virtualization and rendering optimization
- Query optimization strategies
State Management
📄 Read: references/state-persistence.md
- Save and restore grid state
- LocalStorage integration
- Custom state management
Foreign-key
📄 Read: references/foreign-keys.md
- Foreign key column configuration
- Display text mapping for FK values
- Dropdown editors for FK fields
- Cascading foreign key relationships
- Remote data binding for FK data
Row Drag and Drop
📄 Read: references/row-drag-drop.md
- Hierarchical drag-and-drop operations
- Parent-child relationship updates
- Circular hierarchy prevention
- Drop event validation and constraints
- Visual feedback during drag operations
Validation
📄 Read: references/validation.md
- Built-in and custom validation rules
- Async validation against server
- Cross-field validation patterns
- Server-side validation strategies
- Error display and highlighting
- Conditional validation rules
Globalization
📄 Read: references/globalization.md
- Localization and language support
- RTL (Right-to-Left) support
- Cultural formatting and date/number formats
Loading Animation
📄 Read: references/loading-animation.md
- Loading spinner configuration
- Custom loading templates
- Loading animation customization
Accessibility & Compliance
📄 Read: references/accessibility.md
- WCAG 2.1 Level AA conformance
- ARIA labels and attributes
- Keyboard navigation and screen reader support
- Focus management and visual indicators
- Accessibility testing guidance
Quick Start Example
import { Component, ViewChild } from '@angular/core';
import { TreeGridComponent } from '@syncfusion/ej2-angular-treegrid';
import { PageService, SortService, FilterService, EditService } from '@syncfusion/ej2-angular-treegrid';
@Component({
selector: 'app-treegrid',
template: `
<ejs-treegrid
#treegrid
[dataSource]='data'
[childMapping]='childMapping'
[allowPaging]='true'
[allowSorting]='true'
[allowFiltering]='true'
<e-columns>
<e-column field='TaskID' headerText='Task ID' width='90' isPrimaryKey='true'></e-column>
<e-column field='TaskName' headerText='Task Name' width='200'></e-column>
</e-columns>
</ejs-treegrid>
`,
providers: [PageService, SortService, FilterService, EditService]
})
export class AppComponent {
@ViewChild('treegrid') treegrid!: TreeGridComponent;
public data: Object[] = [
{
TaskID: 1,
TaskName: 'Planning',
subtasks: [
{ TaskID: 2, TaskName: 'Scope', Duration: 4, Progress: 100 },
{ TaskID: 3, TaskName: 'Budget', Duration: 4, Progress: 100 }
]
}
];
public childMapping: string = 'subtasks';
public editSettings = { mode: 'Cell', allowEditing: true, allowDeleting: true, allowAdding: true };
}Best Practices
1. Performance Optimization
- Use virtual scrolling for large datasets (10,000+ records)
- Implement server-side operations (sorting, filtering, paging)
- Use frozen columns sparingly to avoid layout shifts
2. Data Management
- Always provide a primary key (isPrimaryKey='true')
- Use ChildMapping for hierarchical data
- Implement proper error handling for API calls
3. User Experience
- Provide loading indicators during data fetch
- Implement search and filter for data discovery
- Show validation messages for editing errors
- Support keyboard navigation and accessibility
4. Customization
- Use themes for consistent styling
- Create reusable cell templates for common patterns
- Style conditional rows for user guidance
- Implement responsive design for mobile devices
5. State Management
- Enable state persistence for user preferences
- Save sort, filter, and column settings
Key Props Summary
| Prop | Type | Default | Use Case |
|---|---|---|---|
dataSource | DataManager\ | Object[] | null |
childMapping | string | null | Property for child records (must be explicitly set) |
allowPaging | boolean | false | Enable pagination |
allowSorting | boolean | false | Enable sorting |
allowFiltering | boolean | false | Enable filtering |
editSettings | EditSettings | {} | Configure edit mode |
enableVirtualization | boolean | false | Enable virtual scrolling |
Module Injection
Must inject required services:
import { PageService, SortService, FilterService, EditService, ExcelExportService, PdfExportService,
PrintService, AggregateService, ToolbarService } from '@syncfusion/ej2-angular-treegrid';
@Component({
providers: [
PageService,
SortService,
FilterService,
EditService,
ExcelExportService,
PdfExportService,
PrintService,
AggregateService,
ToolbarService
]
})Accessibility (a11y)
Implement accessible TreeGrid with WCAG 2.1 Level AA conformance for inclusive user experiences.
When to Use
Use accessibility features when you need to:
- Keyboard navigation — Enable full keyboard-only usage without a mouse
- ARIA labels — Provide semantic meaning to assistive technologies
- Accessibility testing — Validate compliance with tools and standards
Table of Contents
ARIA Labels and Attributes
Grid Container ARIA
import { Component } from '@angular/core';
@Component({
selector: 'app-treegrid',
template: `
<ejs-treegrid
#treegrid
[dataSource]='data'
[childMapping]='childMapping'
role='grid'
aria-label='Task Management TreeGrid'
aria-describedby='grid-description'>
<e-columns>
<e-column
field='TaskID'
headerText='Task ID'
role='columnheader'
aria-label='Task Identifier'
width='90'>
</e-column>
<e-column
field='TaskName'
headerText='Task Name'
role='columnheader'
aria-label='Task Name or Description'
width='200'>
</e-column>
<e-column
field='Priority'
headerText='Priority'
role='columnheader'
aria-label='Task Priority Level'
width='100'>
</e-column>
</e-columns>
</ejs-treegrid>
<div id='grid-description' style='display:none;'>
Interactive task hierarchy with filtering, sorting, and editing capabilities
</div>
`
})
export class AppComponent {
public data: Object[] = [];
public childMapping: string = 'subtasks';
}Row and Cell ARIA
onRowDataBound(args: any) {
// Add ARIA attributes to rows
args.row.setAttribute('role', 'row');
args.row.setAttribute('aria-rowindex', args.rowIndex + 1);
// Add ARIA to cells
const cells = args.row.querySelectorAll('td');
cells.forEach((cell: any, index: number) => {
cell.setAttribute('role', 'gridcell');
cell.setAttribute('aria-colindex', index + 1);
});
}Expand/Collapse ARIA
template: `
<ejs-treegrid
(rowExpanding)='onRowExpanding($event)'
(rowCollapsing)='onRowCollapsing($event)'>
<!-- columns -->
</ejs-treegrid>
`
onRowExpanding(args: any) {
const expandBtn = args.row.querySelector('[role="button"]');
if (expandBtn) {
expandBtn.setAttribute('aria-expanded', 'true');
expandBtn.setAttribute('aria-label', `Expand ${args.data.TaskName}`);
}
}
onRowCollapsing(args: any) {
const expandBtn = args.row.querySelector('[role="button"]');
if (expandBtn) {
expandBtn.setAttribute('aria-expanded', 'false');
expandBtn.setAttribute('aria-label', `Collapse ${args.data.TaskName}`);
}
}---
Keyboard Navigation
Enable Complete Keyboard Support
@Component({
selector: 'app-treegrid',
template: `
<ejs-treegrid
#treegrid
[dataSource]='data'
[childMapping]='childMapping'
>
<e-columns>
<e-column field='TaskID' headerText='Task ID' width='90'></e-column>
<e-column field='TaskName' headerText='Task Name' width='200'></e-column>
</e-columns>
</ejs-treegrid>
`
})
export class AppComponent {
@ViewChild('treegrid') treegrid!: TreeGridComponent;
public data: Object[] = [];
public childMapping: string = 'subtasks';
}Best Practices Summary
1. ARIA First - Use semantic HTML and ARIA to describe structure 2. Keyboard Support - All functionality must work without mouse 3. Screen Readers - Test with actual screen readers, not just assertions
---
Adaptive Design
Adaptive design makes TreeGrid responsive and mobile-friendly with automatic layout adjustments.
When to Use
Use adaptive design when you need to:
- Mobile support — Make TreeGrid work on phones and tablets
- Portrait/landscape — Reflow layout for orientation changes
- Cross-device consistency — Work seamlessly on all devices
Table of Contents
Enable Adaptive
Responsive Layout
import { Component } from '@angular/core';
@Component({
selector: 'app-treegrid',
template: `
<ejs-treegrid
[dataSource]='data'
[childMapping]='childMapping'
[enableAdaptiveUI]='true'
[height]='auto'>
<e-columns>
<e-column field='TaskID' headerText='Task ID' width='90'></e-column>
<e-column field='TaskName' headerText='Task Name' width='200'></e-column>
</e-columns>
</ejs-treegrid>
`
})
export class AppComponent {
public data: Object[] = [];
public childMapping: string = 'subtasks';
}Responsive Breakpoints
Adaptive Columns
public data: Object[] = [];
// Adjust columns based on screen size
adjustColumns() {
if (window.innerWidth < 768) {
// Show only essential columns on mobile
}
}CSS Media Queries
@media (max-width: 768px) {
.e-treegrid {
font-size: 12px;
}
.e-treegrid .e-columnheader {
height: 40px;
}
}Data Adaptors in Angular TreeGrid
Table of Contents
- When to Use This Skill
- Critical: childMapping Property
- URL Adaptor
- ODataV4 Adaptor
- WebAPI Adaptor
- Custom Adaptor
- RemoteSave Adaptor
- Error Handling
When to Use This Skill
Use this skill when you need to:
- Connect to REST APIs with hierarchical data — Configure TreeGrid to fetch parent-child relationships from REST APIs
- Integrate with OData services — Use ODataV4 adaptor for OData-compliant APIs returning tree-structured data
- WebAPI integration — Connect to .NET WebAPI endpoints with hierarchical data support
- Custom backend protocols — Build custom adaptors for proprietary hierarchical data systems
- Server-side operations — Offload filtering, sorting, paging, and expansion to the server
- Error handling — Implement robust error handling for TreeGrid data operations
- Cross-domain requests — Configure CORS and cross-domain data fetching with hierarchical data
Critical: childMapping Property
⚠️ MANDATORY FOR TREEGRID: TreeGrid requires the childMapping property to establish parent-child relationships.
<ejs-treegrid [dataSource]="dataManager"
childMapping="subtasks">
<e-columns>
<!-- columns -->
</e-columns>
</ejs-treegrid>The childMapping property specifies which field in your data contains the child items array:
// Data structure
{
taskID: 1,
taskName: 'Project Planning',
subtasks: [ // <-- This field is specified in childMapping
{
taskID: 2,
taskName: 'Define requirements',
subtasks: []
}
]
}Key Points:
childMappingmust match the property name in your data exactly (case-sensitive)- If omitted, TreeGrid will treat data as flat (no hierarchy)
- The property can be renamed:
childMapping="children",childMapping="sub", etc. - All level objects must have the same childMapping property name
URL Adaptor
Simplest adaptor for REST APIs with standard HTTP GET/POST requests returning hierarchical data.
Setup
import { Component } from '@angular/core';
import { DataManager, UrlAdaptor } from '@syncfusion/ej2-data';
@Component({
selector: 'app-url-adaptor-treegrid',
template: `<ejs-treegrid [dataSource]="dataManager"
childMapping="subtasks">
<e-columns>
<!-- columns -->
</e-columns>
</ejs-treegrid>`
})
export class UrlAdaptorTreeGridComponent {
public dataManager = new DataManager({
url: 'url',
adaptor: new UrlAdaptor(),
crossDomain: true
});
}Server Expectations
The server expects:
- GET request for reading data
- Query parameters:
$skip,$top,$orderby,$filter
Example URL:
GET url?$skip=0&$top=12&$orderby=taskID%20desc&$filter=priority%20gt%202Server Response Format (Hierarchical)
{
"d": [
{
"taskID": 1,
"taskName": "Project Planning",
"duration": 5,
"progress": 85,
"subtasks": [
{
"taskID": 2,
"taskName": "Define requirements",
"duration": 2,
"progress": 90,
"subtasks": []
}
]
}
],
"__count": 5
}Or with array response:
[
{
"taskID": 1,
"taskName": "Project Planning",
"duration": 5,
"subtasks": [
{
"taskID": 2,
"taskName": "Gather requirements",
"duration": 2,
"subtasks": []
}
]
}
]Example Implementation
import { Component } from '@angular/core';
import { DataManager, UrlAdaptor } from '@syncfusion/ej2-data';
@Component({
selector: 'app-treegrid',
template: `
<ejs-treegrid [dataSource]="dataManager"
childMapping="subtasks"
[allowPaging]="true"
[allowSorting]="true"
[allowFiltering]="true">
<e-inject [services]="[Page, Sort, Filter, Edit]"></e-inject>
</ejs-treegrid>
`
})
export class UrlAdaptorTreeGridComponent {
public dataManager = new DataManager({
url: 'url',
adaptor: new UrlAdaptor(),
crossDomain: true
});
}Important Note: Ensure your backend API returns data with the exact subtasks property name (or whatever you specify in childMapping).
---
ODataV4 Adaptor
Specialized for OData v4 protocol. Provides advanced filtering, sorting, and server-side operations with hierarchical data support.
Setup
import { Component } from '@angular/core';
import { DataManager, ODataV4Adaptor } from '@syncfusion/ej2-data';
@Component({
selector: 'app-treegrid',
template: `<ejs-treegrid [dataSource]="dataManager"
childMapping="subtasks">
<e-columns>
<!-- columns -->
</e-columns>
</ejs-treegrid>`
})
export class ODataV4TreeGridComponent {
public dataManager = new DataManager({
url: 'url',
adaptor: new ODataV4Adaptor(),
pageSize: 12
});
}Benefits
- Advanced Filtering: Complex filter expressions across hierarchy
- Selective Loading: Only required columns via $select
- Server-Side Aggregates: Count, sum, average on server
- Relationship Navigation: Access related entities via $expand
Query Parameters
$select - Select specific columns
$filter - Complex filter expressions
$orderby - Sort by one or more fields
$skip - Skip N records (pagination)
$top - Return N records
$count - Include total count
$expand - Include related data (child records)WebAPI Adaptor
For ASP.NET Web API services using RESTful conventions with hierarchical data.
Setup
import { Component } from '@angular/core';
import { DataManager, WebApiAdaptor } from '@syncfusion/ej2-data';
import { TreeGridComponent, Inject, Edit, Toolbar, Page, EditSettingsModel, ToolbarItems } from '@syncfusion/ej2-angular-treegrid';
@Component({
selector: 'app-treegrid-crud',
template: `
<ejs-treegrid #treegrid
[dataSource]="dataManager"
childMapping="subtasks"
[editSettings]="editSettings"
[toolbar]="toolbar"
(actionFailure)="onActionFailure($event)">
<e-columns>
<e-column field="taskID" headerText="Task ID" width="90" [isPrimaryKey]="true"></e-column>
<e-column field="taskName" headerText="Task Name" width="180"></e-column>
<e-column field="duration" headerText="Duration" width="100"></e-column>
<e-column field="progress" headerText="Progress" width="100"></e-column>
</e-columns>
<e-inject [services]="[Edit, Toolbar, Page]"></e-inject>
</ejs-treegrid>
`
})
export class WebApiCrudTreeGridComponent {
@ViewChild('treegrid') treeGridInstance: TreeGridComponent;
public dataManager = new DataManager({
url: 'url',
adaptor: new WebApiAdaptor(),
insertUrl: 'url/insert',
updateUrl: 'url/update',
removeUrl: 'url/remove'
});
public editSettings: EditSettingsModel = {
allowEditing: true,
allowAdding: true,
allowDeleting: true,
mode: 'Dialog'
};
public toolbar: ToolbarItems[] = ['Add', 'Edit', 'Delete', 'Update', 'Cancel'];
onActionFailure(args: any) {
console.error('TreeGrid Error:', args);
}
}Important Notes:
- Ensure the
Subtasksproperty in your C# Task class matches thechildMappingvalue ("subtasks") - Include [isPrimaryKey] on the ID column for proper CRUD operations
- Handle cascade delete if child records should be removed with parent
---
Custom Adaptor
For non-standard backend implementations or specialized hierarchical data processing.
Creating a Custom Adaptor for Hierarchical Data
import { DataManager, Adaptor, Query } from '@syncfusion/ej2-data';
class CustomTreeAdaptor extends Adaptor {
processQuery(dm, query, hierarchyIndex) {
// Custom query processing for tree data
const req = this.baseUrl;
const filter = query.params.filter || '';
const sort = query.params.sort || '';
const page = query.params.pageIndex || 1;
const pageSize = query.params.pageSize || 12;
const expandIds = query.params.expandIds || []; // For lazy loading children
return {
type: 'GET',
url: `${req}?filter=${filter}&sort=${sort}&page=${page}&pageSize=${pageSize}&expandIds=${expandIds.join(',')}`
};
}
processResponse(data, dm, query, xhr, request, key) {
// Process response to hierarchical format
if (data.result) {
return {
result: this.buildHierarchy(data.result),
count: data.totalCount
};
}
return data;
}
buildHierarchy(flatData) {
// Convert flat data to hierarchical using parent-child IDs
const map = new Map();
const roots = [];
// First pass: create map of all items
flatData.forEach(item => {
map.set(item.id, { ...item, subtasks: [] });
});
// Second pass: build hierarchy
flatData.forEach(item => {
if (item.parentId) {
const parent = map.get(item.parentId);
if (parent) {
parent.subtasks.push(map.get(item.id));
}
} else {
roots.push(map.get(item.id));
}
});
return roots;
}
insert(dm, value, tableName, key) {
return {
type: 'POST',
url: dm.insertUrl || dm.baseUrl,
data: JSON.stringify(value),
contentType: 'application/json'
};
}
update(dm, keyField, value, tableName, key) {
return {
type: 'PUT',
url: `${dm.updateUrl || dm.baseUrl}/${value[keyField]}`,
data: JSON.stringify(value),
contentType: 'application/json'
};
}
remove(dm, keyField, value, tableName, key) {
return {
type: 'DELETE',
url: `${dm.removeUrl || dm.baseUrl}/${value[keyField]}`
};
}
}
const data = new DataManager({
url: 'url',
adaptor: new CustomTreeAdaptor()
});RemoteSave Adaptor
Specialized for batch CRUD operations (multiple insert/update/delete in single request) with hierarchical data.
Setup
import { DataManager, RemoteSaveAdaptor } from '@syncfusion/ej2-data';
import { TreeGridComponent, Inject, Edit, Toolbar } from '@syncfusion/ej2-angular-treegrid';
const data = new DataManager({
url: 'url',
adaptor: new RemoteSaveAdaptor(),
batchUrl: 'url/batch'
});
export class BatchTreeGridComponent implements OnInit {
@ViewChild('treegrid') treeGridInstance: TreeGridComponent;
data = dataManager;
editSettings: any = {
allowEditing: true,
allowAdding: true,
allowDeleting: true,
mode: 'Batch'
};
toolbar: string[] = ['Add', 'Edit', 'Delete', 'Update', 'Cancel'];
}
<ejs-treegrid [dataSource]="data"
childMapping="subtasks"
[editSettings]="editSettings"
[toolbar]="toolbar">
<e-columns>
<!-- columns -->
</e-columns>
<e-inject [services]="[Edit, Toolbar]"></e-inject>
</ejs-treegrid>Error Handling
Global Error Handler for TreeGrid
import { Component, ViewChild } from '@angular/core';
import { DataManager, UrlAdaptor } from '@syncfusion/ej2-data';
import { TreeGridComponent } from '@syncfusion/ej2-angular-treegrid';
const data = new DataManager({
url: 'url',
adaptor: new UrlAdaptor()
});
@Component({
selector: 'app-treegrid-error',
template: `
<ejs-treegrid #treegrid
[dataSource]="data"
childMapping="subtasks"
(actionFailure)="onActionFailure($event)">
<e-columns>
<!-- columns -->
</e-columns>
</ejs-treegrid>
`
})
export class ErrorHandlingTreeGridComponent {
@ViewChild('treegrid') treeGridInstance: TreeGridComponent;
data = data;
onActionFailure(args: any) {
console.error('TreeGrid Error:', args);
}
}Aggregates
Aggregates calculate summary statistics like sum, average, minimum, and maximum.
When to Use
Use aggregates when you need to:
- Summary statistics — Calculate sum, average, min, max
- Group totals — Show subtotals for grouped data
- Footer summaries — Display totals in footer rows
- Bottom summaries — Show aggregate for entire dataset
- Custom calculations — Create custom aggregate functions
- Child aggregates — Calculate aggregates for child records
Table of Contents
Aggregate Types
Sum Aggregate
import { Component } from '@angular/core';
import { AggregateService } from '@syncfusion/ej2-angular-treegrid';
@Component({
selector: 'app-treegrid',
template: `
<ejs-treegrid
[dataSource]='data'
[childMapping]='childMapping'
[aggregates]='aggregateRows'>
<e-columns>
<e-column field='TaskID' headerText='Task ID' width='90'></e-column>
<e-column field='Duration' headerText='Duration' width='100'></e-column>
</e-columns>
</ejs-treegrid>
`,
providers: [AggregateService]
})
export class AppComponent {
public data: Object[] = [];
public childMapping: string = 'subtasks';
public aggregateRows: AggregateRowModel[] = [{
columns: [
{
field: 'Duration',
type: 'Sum',
footerTemplate: 'Total: ${Sum}'
}
]
}];
}Average Aggregate
public aggregateRows: AggregateRowModel[] = [{
columns: [{
field: 'Duration',
type: 'Average',
footerTemplate: 'Average: ${Average}'
}]
}];Min/Max Aggregate
public aggregateRows: AggregateRowModel[] = [{
columns: [
{
field: 'Duration',
type: 'Min',
footerTemplate: 'Minimum: ${Min}'
},
{
field: 'Duration',
type: 'Max',
footerTemplate: 'Maximum: ${Max}'
}
]
}];Count Aggregate
public aggregateRows: AggregateRowModel[] = [{
columns: [{
field: 'TaskID',
type: 'Count',
footerTemplate: 'Total Records: ${Count}'
}]
}];Configure Aggregates
Multiple Aggregates
public aggregateRows: AggregateRowModel[] = [{
columns: [
{
field: 'Duration',
type: 'Sum',
footerTemplate: 'Total: ${Sum}'
},
{
field: 'Duration',
type: 'Average',
footerTemplate: 'Avg: ${Average}'
}
]
}];Aggregate Templates
Custom Footer Template
public aggregateRows: AggregateRowModel[] = [{
columns: [{
field: 'Duration',
type: 'Sum',
footerTemplate: '<div style="color: blue;"><b>Total Duration: ${Sum} days</b></div>'
}]
}];Cells
Cells are individual data elements within rows that can be customized with templates, styles, and formats.
When to Use
Use cell features when you need to:
- Apply cell-specific styling — Color-code cells based on values or conditions
- Custom cell templates — Render custom HTML or components in cells
- Cell formatting — Format numbers, dates, currency in cells
- Cell tooltips — Show informative tooltips on hover
- Cell validation — Validate cell values before editing
- Conditional rendering — Show/hide content based on cell values
- Cell events — Handle click, double-click, and edit events
Table of Contents
Cell Styling
queryCellInfo Event
import { Component } from '@angular/core';
import { QueryCellInfoEventArgs, Column } from '@syncfusion/ej2-angular-grids';
@Component({
selector: 'app-treegrid',
template: `
<ejs-treegrid
[dataSource]='data'
[childMapping]='childMapping'
(queryCellInfo)='onQueryCellInfo($event)'>
<e-columns>
<e-column field='TaskID' headerText='Task ID' width='90'></e-column>
<e-column field='Status' headerText='Status' width='100'></e-column>
</e-columns>
</ejs-treegrid>
`
})
export class AppComponent {
public data: Object[] = [];
public childMapping: string = 'subtasks';
onQueryCellInfo(args: any) {
if (args.column.field === 'Status') {
if (args.data.Status === 'Completed') {
args.cell.classList.add('status-completed');
}
}
}
}CSS Classes
.status-completed {
background-color: #C8E6C9;
color: #2E7D32;
font-weight: bold;
}
.status-pending {
background-color: #FFF9C4;
color: #F57F17;
}Formatting
Number Format
<e-column field='Amount' headerText='Amount' type='number' format='C2' width='100'></e-column>Date Format
<e-column field='StartDate' headerText='Start Date' type='date' format='yMd' width='130'></e-column>Clipboard
Clipboard functionality enables copy and paste operations with custom formatting options.
When to Use
Use clipboard features when you need to:
- Copy data — Allow users to copy cells or rows
- Paste data — Enable pasting data into TreeGrid
- Bulk data entry — Paste from spreadsheets or other sources
- Keyboard shortcuts — Support Ctrl+C/Ctrl+V operations
- Custom paste — Handle special formatting on paste
Copy and Paste
Enable Copy
import { Component, ViewChild } from '@angular/core';
import { TreeGridComponent, ToolbarService } from '@syncfusion/ej2-angular-treegrid';
@Component({
selector: 'app-treegrid',
template: `
<ejs-treegrid
#treegrid
[dataSource]='data'
[childMapping]='childMapping'
[toolbar]='toolbar'
<e-columns>
<e-column field='TaskID' headerText='Task ID' width='90'></e-column>
<e-column field='TaskName' headerText='Task Name' width='200'></e-column>
</e-columns>
</ejs-treegrid>
`,
providers: [ ToolbarService ]
})
export class AppComponent {
@ViewChild('treegrid') treegrid!: TreeGridComponent;
public data: Object[] = [];
public childMapping: string = 'subtasks';
public toolbar: Object[] = [{ text: 'Copy', tooltipText: 'Copy', prefixIcon: 'e-copy', id: 'copy' },
{ text: 'Copy With Header', tooltipText: 'Copy With Header', prefixIcon: 'e-copy', id: 'copyHeader' }];
public toolbarClick(args: ClickEventArgs): void {
if(this.treegrid.getSelectedRecords().length>0) {
let withHeader: boolean = false;
if (args.item.id === 'copyHeader') {
withHeader = true;
}
this.treegrid.copy(withHeader);
} else {
this.alertDialog.show();
}
}
}Column Chooser
Column Chooser allows users to dynamically show or hide columns at runtime through a user-friendly dialog interface.
When to Use
Use Column Chooser when you need to:
- Control column visibility — Let users show/hide columns as needed
- Personalize view — Let users customize their preferred column layout
- Default columns — Define which columns are visible by default
- Dialog interface — Provide accessible column management UI
Table of Contents
- Enable Column Chooser
- Hide Column in Column Chooser
- Open Column Chooser Externally
- Custom Column Chooser Templates
Enable Column Chooser
import { Component } from '@angular/core';
import { ColumnChooserService, ToolbarService } from '@syncfusion/ej2-angular-treegrid';
@Component({
selector: 'app-treegrid',
template: `
<ejs-treegrid
[dataSource]='data'
[toolbar]='toolbar'
[showColumnChooser]='true'>
</ejs-treegrid>
`,
providers: [ColumnChooserService, ToolbarService]
})
export class AppComponent {
public data: Object[] = [];
public toolbar: string[] = ['ColumnChooser'];
}Hide Column in Column Chooser
import { Component, ViewChild } from '@angular/core';
import { TreeGridComponent, ColumnChooserService, ToolbarService } from '@syncfusion/ej2-angular-treegrid';
@Component({
selector: 'app-treegrid',
template: `
<ejs-treegrid
#treegrid
[dataSource]='data'
[toolbar]='toolbar'
[showColumnChooser]='true'
childMapping='subtasks'>
<e-columns>
<e-column field='TaskID' headerText='Task ID' [isPrimaryKey]='true' width='90'></e-column>
<e-column field='TaskName' headerText='Task Name' [showInColumnChooser]='false' width='200'></e-column>
</e-columns>
</ejs-treegrid>
`,
providers: [ColumnChooserService, ToolbarService]
})
export class AppComponent {
public data: Object[] = [];
public toolbar: string[] = ['ColumnChooser'];
}Open Column Chooser Externally
import { Component, ViewChild } from '@angular/core';
import { TreeGridComponent, ColumnChooserService, ToolbarService } from '@syncfusion/ej2-angular-treegrid';
@Component({
selector: 'app-treegrid',
template: `
<button (click)='openColumnChooser()' class='e-btn e-flat'>Open Column Chooser</button>
<ejs-treegrid
#treegrid
[dataSource]='data'
[showColumnChooser]='true'
childMapping='subtasks'>
</ejs-treegrid>
`,
providers: [ColumnChooserService, ToolbarService]
})
export class AppComponent {
@ViewChild('treegrid') treegrid!: TreeGridComponent;
public data: Object[] = [];
openColumnChooser(): void {
// Open column chooser at X: 200, Y: 50 coordinates
(this.treegrid as TreeGridComponent).columnChooserModule.openColumnChooser(200, 50);
}
}Custom Column Chooser Templates
Using Header, Content, and Footer Templates
import { Component, ViewChild } from '@angular/core';
import { TreeGridComponent, ColumnChooserService, ToolbarService } from '@syncfusion/ej2-angular-treegrid';
import { ButtonModule } from '@syncfusion/ej2-angular-buttons';
import { NgIf } from '@angular/common';
@Component({
selector: 'app-treegrid',
template: `
<ejs-treegrid
#treegrid
[dataSource]='data'
[toolbar]='toolbar'
[showColumnChooser]='true'
childMapping='subtasks'>
<!-- Custom Header Template -->
<ng-template #columnChooserSettingsHeaderTemplate>
<div style='padding: 10px;'>
<h4>Column Options</h4>
</div>
</ng-template>
<!-- Custom Content Template -->
<ng-template #columnChooserSettingsTemplate let-data>
<div *ngIf='!(data && data.columns && data.columns.length)' class='no-record-text'>
No Columns Found
</div>
<div *ngIf='(data && data.columns && data.columns.length)'>
<div *ngFor='let column of data.columns' style='padding: 5px;'>
<input type='checkbox' [checked]='column.visible' />
<label>{{ column.headerText }}</label>
</div>
</div>
</ng-template>
<!-- Custom Footer Template -->
<ng-template #columnChooserSettingsFooterTemplate>
<div style='padding: 10px; border-top: 1px solid #ddd;'>
<button (click)='applyChanges()' class='e-btn e-primary'>Apply</button>
<button (click)='closeDialog()' class='e-btn'>Close</button>
</div>
</ng-template>
</ejs-treegrid>
`,
providers: [ColumnChooserService, ToolbarService],
standalone: true,
imports: [ButtonModule, NgIf]
})
export class AppComponent {
@ViewChild('treegrid') treegrid!: TreeGridComponent;
public data: Object[] = [];
public toolbar: string[] = ['ColumnChooser'];
applyChanges(): void {
// Apply custom logic for column visibility changes
console.log('Applying column changes...');
}
closeDialog(): void {
// Close the column chooser dialog
(this.treegrid as TreeGridComponent).grid.columnChooserModule.hideDialog();
}
}Column Menu
Column Menu provides a context menu for column headers enabling sorting, filtering, and custom operations on columns.
When to Use
Use column menu when you need to:
- Sort from header — Right-click column header to sort
- Filter from header — Quick filtering from column menu
- Column grouping — Group columns from menu
- Custom operations — Add domain-specific column actions
- Column visibility — Show/hide columns from menu
- Column ordering — Reorder columns via menu
- Conditional menu items — Show different options per column
Table of Contents
Enable Column Menu
Basic Column Menu
import { Component } from '@angular/core';
import { ColumnMenuService, FilterService, SortService } from '@syncfusion/ej2-angular-treegrid';
@Component({
selector: 'app-treegrid',
template: `
<ejs-treegrid
[dataSource]='data'
[childMapping]='childMapping'
[showColumnMenu]='true'
[allowFiltering]='true'
[allowSorting]='true'>
<e-columns>
<e-column field='TaskID' headerText='Task ID'></e-column>
<e-column field='TaskName' headerText='Task Name'></e-column>
<e-column field='Duration' headerText='Duration'></e-column>
<e-column field='Priorty' headerText='Priority'></e-column>
</e-columns>
</ejs-treegrid>
`,
providers: [ColumnMenuService, FilterService, SortService]
})
export class AppComponent {
public data: Object[] = [];
public childMapping: string = 'subtasks';
}Built-in Menu Items
Auto-available Menu Items
// When enabled, column menu shows:
- Sort Ascending
- Sort Descending
- Filter
- Filter bar (if filtering enabled)
- Column chooser (if column menu service injected)
- Autofit (resize column to content)
- Autofit All ColumnsCustom Menu Items
Add Custom Menu Items
import { Component, ViewChild } from '@angular/core';
import { TreeGridComponent } from '@syncfusion/ej2-angular-treegrid';
@Component({
selector: 'app-treegrid',
template: `
<ejs-treegrid
#treegrid
[dataSource]='data'
[showColumnMenu]='true'
[columnMenuItems]='menuItems'
(columnMenuClick)='onColumnMenuClick($event)'>
<e-columns>
<e-column field='TaskID' headerText='Task ID'></e-column>
<e-column field='TaskName' headerText='Task Name'></e-column>
</e-columns>
</ejs-treegrid>
`,
providers: [ColumnMenuService, FilterService, SortService]
})
export class AppComponent {
@ViewChild('treegrid') treegrid!: TreeGridComponent;
public data: Object[] = [];
public menuItems = [
'SortAscending',
'SortDescending',
{ text: 'Export Column', id: 'exportcolumn' },
{ text: 'Copy Column', id: 'copycolumn' },
'Filter'
];
onColumnMenuClick(args: any): void {
if (args.item.id === 'exportcolumn') {
const fieldName = args.column.field;
console.log('Exporting column:', fieldName);
this.exportColumn(fieldName);
} else if (args.item.id === 'copycolumn') {
const fieldName = args.column.field;
this.copyColumnData(fieldName);
}
}
private exportColumn(fieldName: string): void {
// Export logic
}
private copyColumnData(fieldName: string): void {
// Copy logic
}
}Menu Events
Handle Column Menu Operations
import { Component, ViewChild } from '@angular/core';
import { TreeGridComponent } from '@syncfusion/ej2-angular-treegrid';
import { ColumnMenuService, FilterService, SortService } from '@syncfusion/ej2-angular-treegrid';
@Component({
template: `
<ejs-treegrid
#treegrid
[dataSource]='data'
[showColumnMenu]='true'
[allowFiltering]='true'
[allowSorting]='true'
(columnMenuClick)='onMenuClick($event)'>
</ejs-treegrid>
`,
providers: [ColumnMenuService, FilterService, SortService]
})
export class AppComponent {
@ViewChild('treegrid') treegrid!: TreeGridComponent;
public data: Object[] = [];
onMenuClick(args: any): void {
const columnName = args.column.field;
const menuItem = args.item.text;
if (menuItem === 'Sort Ascending') {
console.log(`Sorting ${columnName} ascending`);
} else if (menuItem === 'Sort Descending') {
console.log(`Sorting ${columnName} descending`);
} else if (menuItem === 'Filter') {
console.log(`Opening filter for ${columnName}`);
}
}
}Dynamic Menu Control
Conditionally Show/Hide Menu Items
import { Component } from '@angular/core';
import { ColumnMenuService, FilterService, SortService } from '@syncfusion/ej2-angular-treegrid';
@Component({
selector: 'app-treegrid',
template: `
<ejs-treegrid
[dataSource]='data'
[showColumnMenu]='true'
[columnMenuItems]='getMenuItems.bind(this)'>
</ejs-treegrid>
`,
providers: [ColumnMenuService, FilterService, SortService]
})
export class AppComponent {
public data: Object[] = [];
public userRole: string = 'viewer'; // admin, editor, viewer
getMenuItems(column: any): string[] {
const defaultItems = ['SortAscending', 'SortDescending', 'Filter'];
if (this.userRole === 'admin') {
return [...defaultItems, 'ColumnChooser'];
} else if (this.userRole === 'editor') {
return defaultItems;
} else {
return ['SortAscending', 'SortDescending']; // Viewers can only sort
}
}
}Column-specific Menu Items
public getMenuItems(column: any): string[] {
const defaultItems = ['SortAscending', 'SortDescending'];
// Only show filter for text columns
if (column.type === 'text' || column.type === 'string') {
defaultItems.push('Filter');
}
// Numbers can use numeric filters
if (column.type === 'number') {
defaultItems.push('Filter', { text: 'Statistical', id: 'stats' });
}
return defaultItems;
}Column Spanning
Column Spanning allows merging of adjacent cells in single row to organize data more effectively in grid layouts.
When to Use
Use column spanning when you need to:
- Merge cells — Combine adjacent cells in a row
- Reduce repetition — Show category headers once for multiple columns
- Visual organization — Improve readability with cell grouping
Table of Contents
Basic Column Spanning
import { Component } from '@angular/core';
@Component({
selector: 'app-treegrid',
template: `
<ejs-treegrid
[dataSource]='data'
[enableColumnSpan]="true"
[childMapping]='childMapping'>
</ejs-treegrid>
`
})
export class AppComponent {
public data: Object[] = [
{
TaskID: 1,
TaskName: 'Planning',
StartDate: '02/03/2017',
Duration: 8,
Progress: 100,
templateID: 1,
subtasks: []
}
];
public childMapping: string = 'subtasks';
}Column Spanning Options
Using QueryCellInfo
import { Component, ViewChild } from '@angular/core';
import { TreeGridComponent } from '@syncfusion/ej2-angular-treegrid';
@Component({
selector: 'app-treegrid',
template: `
<ejs-treegrid
#treegrid
[dataSource]='data'
(queryCellInfo)='onQueryCellInfo($event)'>
</ejs-treegrid>
`
})
export class AppComponent {
@ViewChild('treegrid') treegrid!: TreeGridComponent;
public data: Object[] = [];
onQueryCellInfo(args: any): void {
// Span cells based on conditions
if (args.rowIndex === 0) {
args.colSpan = 2; // Span 2 columns
}
}
}Columns
Columns define the structure and display of TreeGrid data with various customization options.
When to Use
Use column features when you need to:
- Define data structure — Map data fields to columns for display
- Customize column headers — Add icons, tooltips, and custom header templates
- Control column width — Set fixed or auto-calculated column widths
- Hide/show columns — Toggle visibility of specific columns
- Reorder columns — Allow users to drag columns to reorder
- Format column data — Display dates, numbers, currency with proper formatting
- Custom column templates — Render custom HTML or components in columns
Table of Contents
Column Definition
Basic Column Setup
import { Component } from '@angular/core';
@Component({
selector: 'app-treegrid',
template: `
<ejs-treegrid [dataSource]='data' [childMapping]='childMapping'>
<e-columns>
<e-column field='TaskID' headerText='Task ID' width='90'></e-column>
<e-column field='TaskName' headerText='Task Name' width='150'></e-column>
<e-column field='Duration' headerText='Duration' width='100'></e-column>
</e-columns>
</ejs-treegrid>
`
})
export class AppComponent {
public data: Object[] = [];
public childMapping: string = 'subtasks';
}Column Types
Text Column
<e-column field='TaskName' headerText='Task Name' type='sting' width='150'></e-column>Number Column
<e-column field='Duration' headerText='Duration' type='number' width='100'></e-column>Date Column
<e-column field='StartDate' headerText='Start Date' type='date' format='yMd' width='130'></e-column>Boolean Column
<e-column field='IsCompleted' headerText='Completed' type='boolean' width='100'></e-column>Headers and Width
Fixed Width
<e-column field='TaskName' headerText='Task Name' width='200'></e-column>Auto Width
<e-column field='TaskName' headerText='Task Name' width='auto'></e-column>Min/Max Width
<e-column field='TaskName' headerText='Task Name' minWidth='100' maxWidth='300'></e-column>Custom Header
<e-column field='TaskName' headerText='Task Information' width='200'></e-column>Templates and Customization
Column Template
<e-columns>
<e-column field='TaskName' headerText='Task Name' width='150' >
<ng-template #template let-data>
<strong>{{ data.TaskName }}</strong>
</ng-template>
</e-column>
</e-columns>
Header Template
<e-column field='TaskName' width='150'>
<ng-template #headerTemplate>
<div style="text-align: center;">
<strong>Task Information</strong>
</div>
</ng-template>
</e-column>Column Visibility
<e-column field='InternalNotes' headerText='Notes' [visible]='false'></e-column>Column Reordering
<ejs-treegrid
[allowReordering]='true'>
<!-- columns -->
</ejs-treegrid>Frozen Columns
<ejs-treegrid
[frozenColumns]='2'>
<e-column field='TaskID' headerText='Task ID' freeze='Right' width='90'></e-column>
<e-column field='TaskName' headerText='Task Name' freeze='Left' width='150'></e-column>
</ejs-treegrid>Command Column
Command Column displays action buttons like Edit, Delete, Save, and Cancel in cells for quick inline operations on data.
When to Use
Use command column when you need to:
- Action buttons — Add Edit, Delete, Save, Cancel buttons
- Inline operations — Perform operations without dialogs
- Custom commands — Create custom action buttons
- Multiple actions — Provide several options per row
Table of Contents
Basic Command Column
Enable Command Column
import { Component } from '@angular/core';
import { CommandColumnService, EditService } from '@syncfusion/ej2-angular-treegrid';
@Component({
selector: 'app-treegrid',
template: `
<ejs-treegrid
[dataSource]='data'
[childMapping]='childMapping'
[editSettings]='editSettings'>
<e-columns>
<e-column field='TaskID' headerText='ID' width='90' isPrimaryKey='true'></e-column>
<e-column field='TaskName' headerText='Task Name' width='200'></e-column>
<e-column headerText='Action' width='150' [commands]='commands'></e-column>
</e-columns>
</ejs-treegrid>
`,
providers: [CommandColumnService, EditService]
})
export class AppComponent {
public data: Object[] = [];
public childMapping: string = 'subtasks';
public editSettings = {
mode: 'Normal',
allowEditing: true,
allowDeleting: true,
allowAdding: true
};
public commands = [
{ type: 'Edit', buttonOption: { iconCss: 'e-icons e-edit', cssClass: 'e-small' } },
{ type: 'Delete', buttonOption: { iconCss: 'e-icons e-delete', cssClass: 'e-small' } },
{ type: 'Save', buttonOption: { iconCss: 'e-icons e-update', cssClass: 'e-small' } },
{ type: 'Cancel', buttonOption: { iconCss: 'e-icons e-cancel-icon', cssClass: 'e-small' } }
];
}Built-in Commands
Standard Command Types
public commands = [
{ type: 'Edit' }, // Opens edit row
{ type: 'Delete' }, // Deletes row
{ type: 'Save' }, // Saves edited row
{ type: 'Cancel' } // Cancels editing
];Commands with Custom Styling
public commands = [
{
type: 'Edit',
buttonOption: {
iconCss: 'e-icons e-edit',
cssClass: 'e-flat',
content: 'Edit'
}
},
{
type: 'Delete',
buttonOption: {
iconCss: 'e-icons e-delete',
cssClass: 'e-danger',
content: 'Delete'
}
}
];Custom Commands
Add Custom Action Buttons
import { Component, ViewChild } from '@angular/core';
import { TreeGridComponent, CommandColumnService, EditService } from '@syncfusion/ej2-angular-treegrid';
@Component({
selector: 'app-treegrid',
template: `
<ejs-treegrid
#treegrid
[dataSource]='data'
[editSettings]='editSettings'
(commandClick)='onCommandClick($event)'>
<e-columns>
<e-column field='TaskID' headerText='ID' isPrimaryKey='true'></e-column>
<e-column field='TaskName' headerText='Task Name'></e-column>
<e-column
headerText='Actions'
width='150'
[commands]='commands'>
</e-column>
</e-columns>
</ejs-treegrid>
`,
providers: [CommandColumnService, EditService]
})
export class AppComponent {
@ViewChild('treegrid') treegrid!: TreeGridComponent;
public data: Object[] = [];
public editSettings = {
mode: 'Normal',
allowEditing: true
};
public commands = [
{ type: 'Edit', buttonOption: { cssClass: 'e-outline', content: 'Edit' } },
{ type: 'Delete', buttonOption: { cssClass: 'e-danger', content: 'Delete' } },
{
buttonOption: {
content: 'Copy',
cssClass: 'e-info',
iconCss: 'e-icons e-copy'
}
},
{
buttonOption: {
content: 'Details',
cssClass: 'e-primary',
iconCss: 'e-icons e-details'
}
}
];
onCommandClick(args: any): void {
if (args.commandType === 'Edit') {
// Edit row
console.log('Editing row:', args.rowData);
} else if (args.commandType === 'Delete') {
// Delete row
if (confirm('Delete this record?')) {
this.treegrid.deleteRecord(args.rowData);
}
} else if (args.commandType === 'Copy') {
// Custom copy command
const copyData = { ...args.rowData };
copyData.TaskID = null; // Clear ID for new record
console.log('Copying record:', copyData);
this.treegrid.addRecord(copyData);
} else if (args.commandType === 'Details') {
// Show details dialog
console.log('Show details for:', args.rowData);
this.showDetailsDialog(args.rowData);
}
}
private showDetailsDialog(data: any): void {
// Implement details dialog
}
}Command Events
Handle Command Click Events
import { Component } from '@angular/core';
import { CommandColumnService, EditService } from '@syncfusion/ej2-angular-treegrid';
@Component({
selector: 'app-treegrid',
template: `
<ejs-treegrid
[dataSource]='data'
(commandClick)='onCommandClick($event)'
(actionBegin)='onActionBegin($event)'
(actionComplete)='onActionComplete($event)'>
<e-columns>
<!-- Command column -->
</e-columns>
</ejs-treegrid>
`,
providers: [CommandColumnService, EditService]
})
export class AppComponent {
public data: Object[] = [];
onCommandClick(args: any): void {
console.log('Row Data:', args.rowData);
console.log('Command Type:', args.commandType);
console.log('Row Index:', args.rowIndex);
}
onActionBegin(args: any): void {
if (args.requestType === 'save') {
console.log('Saving changes...');
} else if (args.requestType === 'delete') {
console.log('Deleting record...');
}
}
onActionComplete(args: any): void {
if (args.requestType === 'save') {
console.log('Changes saved successfully');
}
}
}Disable Commands for Specific Rows
onCommandClick(args: any): void {
const rowData = args.rowData;
// Disable delete for locked records
if (rowData.IsLocked && args.commandType === 'Delete') {
alert('Cannot delete locked records');
args.cancel = true;
return;
}
// Only allow edit for owned records
if (rowData.OwnerId !== this.currentUserId && args.commandType === 'Edit') {
alert('You can only edit your own records');
args.cancel = true;
return;
}
}Context Menu
Context menu provides quick actions when right-clicking on rows or cells.
When to Use
Use context menu when you need to:
- Quick actions — Right-click menu for common operations
- CRUD operations — Edit, delete, copy from menu
- Custom menus — Add domain-specific menu items
- Conditional items — Show/hide menu items based on context
- Copy to clipboard — Copy cell or row data via menu
- Row operations — Actions specific to selected row
- Touch-friendly — Alternative to right-click on touch devices
Table of Contents
Enable Context Menu
Basic Context Menu
import { Component } from '@angular/core';
import { ContextMenuService, ExcelExportService, EditService, ToolbarService } from '@syncfusion/ej2-angular-treegrid';
@Component({
selector: 'app-treegrid',
template: `
<ejs-treegrid
[dataSource]='data'
[childMapping]='childMapping'
[contextMenuItems]='contextMenuItems'
[editSettings]='editing'
[toolbar]="toolbar"
allowExcelExport='true'>
<e-columns>
<e-column field='TaskID' headerText='Task ID' width='90'></e-column>
<e-column field='TaskName' headerText='Task Name' width='200'></e-column>
</e-columns>
</ejs-treegrid>
`,
providers: [ContextMenuService, ExcelExportService, EditService, ToolbarService]
})
export class AppComponent {
public data: Object[] = [];
public childMapping: string = 'subtasks';
public editing: EditSettingsModel = [ { allowDeleting: true, allowEditing: true, mode: 'Row' } ];
public contextMenuItems = [
'Edit', 'Delete', 'Copy', 'ExcelExprt'
];
}Context Menu Items
Built-in Items
public contextMenuItems = ['AutoFit', 'AutoFitAll', 'SortAscending', 'SortDescending', 'Edit', 'Delete', 'Save', 'Cancel', 'PdfExport', 'ExcelExport', 'CsvExport', 'FirstPage', 'PrevPage', 'LastPage', 'NextPage', 'Indent', 'Outdent'];Custom Menu Items
public contextMenuItems = [
'Edit', 'Delete',
{ text: 'Custom Action', id: 'customcmd', target: '.e-content' }
];Menu Events
Context Menu Click
<ejs-treegrid
(contextMenuClick)='onContextMenuClick($event)'>
<!-- columns -->
</ejs-treegrid>onContextMenuClick(args: any) {
if (args.item.id === 'customcmd') {
console.log('Custom context menu clicked');
console.log('Target data:', args.rowData);
}
}CSV Export
Export TreeGrid data to CSV format with customization options.
When to Use
Use CSV export when you need to:
- Create data files — Export TreeGrid data as CSV
- Lightweight format — Use CSV for simplicity and compatibility
- Exclude columns — Skip certain columns in export
Table of Contents
Basic CSV Export
Simple CSV Export
import { Component, ViewChild } from '@angular/core';
import { TreeGridComponent } from '@syncfusion/ej2-angular-treegrid';
import { ToolbarService, ExcelExportService } from '@syncfusion/ej2-angular-treegrid';
@Component({
selector: 'app-treegrid',
template: `
<button (click)='exportCSV()'>Export as CSV</button>
<ejs-treegrid
#treegrid
[dataSource]='data'
[childMapping]='childMapping'
[toolbar]='["CsvExport"]'
(toolbarClick)='onToolbarClick($event)'>
<e-columns>
<e-column field='TaskID' headerText='Task ID' width='100'></e-column>
<e-column field='TaskName' headerText='Task Name' width='200'></e-column>
<e-column field='Duration' headerText='Duration' width='100'></e-column>
</e-columns>
</ejs-treegrid>
`,
providers: [ToolbarService, ExcelExportService]
})
export class AppComponent {
@ViewChild('treegrid') treegrid!: TreeGridComponent;
public data: Object[] = [];
public childMapping: string = 'subtasks';
exportCSV() {
this.treegrid.csvExport();
}
onToolbarClick(args: any) {
if (args.item.id === this.treegrid.grid.element.id + '_csvexport') {
this.treegrid.csvExport();
}
}
}---
Export Configuration
Customized CSV Export
@Component({
selector: 'app-treegrid',
template: `
<ejs-treegrid
#treegrid
[dataSource]='data'
[childMapping]='childMapping'
[toolbar]='["CsvExport"]'
(toolbarClick)='onToolbarClick($event)'>
<!-- columns -->
</ejs-treegrid>
`
})
export class AppComponent {
@ViewChild('treegrid') treegrid!: TreeGridComponent;
public data: Object[] = [];
public childMapping: string = 'subtasks';
onToolbarClick(args: any) {
if (args.item.id === this.treegrid.grid.element.id + '_csvexport') {
const csvExportProperties = {
fileName: 'tasks.csv',
dataSource: this.data,
columns: [
{ field: 'TaskID', headerText: 'ID' },
{ field: 'TaskName', headerText: 'Task' },
{ field: 'Duration', headerText: 'Duration (days)' },
{ field: 'Progress', headerText: 'Progress (%)' }
],
hierarchyExportMode: 'All', // Export all levels
isCollapsedStatePersist: false,
theme: {
header: { fontColor: '#ffffff', borders: { color: '#000000' } },
record: { fontColor: '#000000' }
}
};
this.treegrid.csvExport(csvExportProperties);
}
}
}Export Current Page Only
const csvExportProperties = {
fileName: 'tasks-page.csv',
exportType: 'CurrentPage' // Only current page
};
this.treegrid.csvExport(csvExportProperties);Export Selected Records
const selectedRecords = this.treegrid.getSelectedRecords();
if (selectedRecords.length === 0) {
alert('Please select records to export');
return;
}
const csvExportProperties = {
fileName: 'selected-tasks.csv',
dataSource: selectedRecords
};
this.treegrid.csvExport(csvExportProperties);---
File Naming
Dynamic File Naming
const timestamp = new Date().toISOString().split('T')[0];
const fileName = `tasks-export-${timestamp}.csv`;
const csvExportProperties = {
fileName: fileName,
dataSource: this.data
};
this.treegrid.csvExport(csvExportProperties);
---
Export Events
Handle Export Events
@Component({
template: `
<ejs-treegrid
(beforeCsvExport)='onBeforeCsvExport($event)'
(csvExportComplete)='onCsvExportComplete($event)'>
<!-- columns -->
</ejs-treegrid>
`
})
export class AppComponent {
onBeforeCsvExport(args: any) {
console.log('CSV export starting');
}
onCsvExportComplete(args: any) {
console.log('CSV export completed');
}
public data: Object[] = [];
}---
Data Binding
Data binding connects the TreeGrid to various data sources including local arrays, remote APIs, and ORMs.
When to Use
Use data binding when you need to:
- Load local data — Bind TreeGrid to in-memory arrays or objects
- Fetch remote data — Load data from REST APIs or OData services
- Server-side operations — Perform filtering, sorting, paging on backend
- Dynamic data updates — Refresh or update TreeGrid data programmatically
- ORM integration — Connect to Entity Framework or other ORMs
- Real-time data — Fetch live data from APIs
- Hierarchical data — Bind parent-child relationships from various sources
Table of Contents
Local Data Binding
Bind Local Array
import { Component } from '@angular/core';
@Component({
selector: 'app-treegrid',
template: `
<ejs-treegrid
[dataSource]='taskData'
[childMapping]='childMapping'>
<e-columns>
<e-column field='TaskID' headerText='Task ID' width='90'></e-column>
<e-column field='TaskName' headerText='Task Name' width='150'></e-column>
<e-column field='Duration' headerText='Duration' width='100'></e-column>
</e-columns>
</ejs-treegrid>
`
})
export class AppComponent {
public childMapping: string = 'subtasks';
public taskData: Object[] = [
{
TaskID: 1,
TaskName: 'Planning',
Duration: 8,
subtasks: [
{ TaskID: 2, TaskName: 'Scope', Duration: 4 },
{ TaskID: 3, TaskName: 'Budget', Duration: 4 }
]
}
];
}Dynamic Data Assignment
setDataSource(data: Object[]) {
this.treegrid.dataSource = data;
this.treegrid.refresh();
}Remote Data Binding
UrlAdaptor
import { DataManager, UrlAdaptor } from '@syncfusion/ej2-data';
export class AppComponent {
public dataManager = new DataManager({
url: 'url',
adaptor: new UrlAdaptor()
});
}With DataManager
<ejs-treegrid
[dataSource]='dataManager'
idMapping='TaskID'
parentIdMapping='ParentItem'
hasChildMapping='isParent'
[allowPaging]='true'
[pageSettings]='{ pageSize: 10 }'>
<!-- columns -->
</ejs-treegrid>ORM Integration
Entity Framework Core
public dataManager = new DataManager({
url: 'url',
adaptor: new ODataV4Adaptor(),
crossDomain: true
});Error Handling
Handle Data Binding Errors
import { Component, ViewChild, OnInit } from '@angular/core';
import { TreeGridComponent, DataManager, UrlAdaptor } from '@syncfusion/ej2-angular-treegrid';
@Component({
selector: 'app-treegrid',
template: `
<div *ngIf='errorMessage' class='error-message'>
{{ errorMessage }}
</div>
<ejs-treegrid
#treegrid
[dataSource]='dataManager'
(actionFailure)='onActionFailure($event)'>
</ejs-treegrid>
`
})
export class AppComponent implements OnInit {
@ViewChild('treegrid') treegrid!: TreeGridComponent;
public dataManager: DataManager = new DataManager({
url: 'url',
adaptor: new UrlAdaptor()
});
public errorMessage: string = '';
ngOnInit(): void {
}
onActionFailure(args: any): void {
if (args.error) {
this.errorMessage = args.error.statusText || 'Data loading failed';
console.error('Data Error:', args.error);
}
}
}Change Detection Optimization
Use OnPush Strategy for Large Datasets
import { Component, ChangeDetectionStrategy, Input, OnInit } from '@angular/core';
import { DataManager, UrlAdaptor } from '@syncfusion/ej2-data';
@Component({
selector: 'app-treegrid',
template: `
<ejs-treegrid
[dataSource]='dataManager'
[childMapping]='childMapping'
[allowVirtualization]='true'>
<e-columns>
<e-column field='TaskID' headerText='Task ID' width='90'></e-column>
<e-column field='TaskName' headerText='Task Name' width='200'></e-column>
</e-columns>
</ejs-treegrid>
`,
changeDetection: ChangeDetectionStrategy.OnPush
})
export class AppComponent implements OnInit {
@Input() dataSource: string = 'url';
public dataManager!: DataManager;
public childMapping: string = 'Children';
ngOnInit(): void {
this.dataManager = new DataManager({
url: this.dataSource,
adaptor: new UrlAdaptor(),
// Enable caching to reduce API calls
enableCaching: true
});
}
}Editing
Editing allows users to create, modify, and delete records with multiple editing modes and validation.
When to Use
Use editing features when you need to:
- Enable CRUD operations — Allow users to create, read, update, and delete records in the TreeGrid
- Inline editing — Edit cell values directly without opening dialogs
- Dialog editing — Provide a dedicated form for editing records with validation
- Batch editing — Edit multiple records and save them all at once
- Field validation — Ensure data integrity with required fields and custom validation rules
- Edit mode customization — Control how editing behaves (e-mail formats, numeric precision, etc.)
- Prevent unwanted edits — Disable editing for specific columns or rows
Table of Contents
CRUD & Editing Rules
Rule 1: EditSettings is MANDATORY for Editing
Severity: 🔴 CRITICAL - Editing won't work without configuration
Requirement:
// ✅ REQUIRED - Must define editSettings
public editSettings = {
mode: 'Cell', // Required: 'Cell', 'Dialog', 'Inline', 'Batch'
allowEditing: true, // Required: Enable editing
allowAdding: true, // Optional: Enable adding rows
allowDeleting: true // Optional: Enable deleting rows
};
<ejs-treegrid
[editSettings]='editSettings'
[primaryKey]='TaskID'> // Also need primary key
</ejs-treegrid>
// ❌ WRONG - No editSettings = No editing possible
<ejs-treegrid>
<!-- No editing functionality -->
</ejs-treegrid>Additional Requirements:
// ✅ MUST inject EditService for any editing mode
import { EditService } from '@syncfusion/ej2-angular-treegrid';
@Component({
providers: [EditService] // REQUIRED
})Edit Mode Rules:
// Cell Mode - Edit individual cells
editSettings: { mode: 'Cell', allowEditing: true }
// Dialog Mode - Edit in popup dialog
editSettings: { mode: 'Dialog', allowEditing: true }
// Inline Mode - Edit full row inline
editSettings: { mode: 'Inline', allowEditing: true }
// Batch Mode - Edit multiple rows, save all at once
editSettings: { mode: 'Batch', allowEditing: true }---
Rule 2: EditService Module Injection is MANDATORY for Editing
Severity: 🔴 CRITICAL - Editing features won't initialize without service
Requirement:
// ✅ REQUIRED - EditService must be injected
import { EditService } from '@syncfusion/ej2-angular-treegrid';
@Component({
selector: 'app-treegrid',
template: `...`,
providers: [EditService] // MANDATORY for editing
})
export class AppComponent {}
// ❌ WRONG - Missing EditService
@Component({
providers: [] // Service not injected = Editing fails
})---
Rule 3: Column EditType Must Match Data Type
Severity: 🟠 IMPORTANT - Validation fails with mismatched editTypes
Requirement:
// ✅ CORRECT - EditType matches data type
<e-columns>
<e-column field='TaskName'
headerText='Task'
editType='stringedit'></e-column>
<e-column field='Duration'
headerText='Duration'
editType='numericedit'
[validationRules]='{ required: true, min: 1, max: 1000 }'></e-column>
<e-column field='Priority'
headerText='Priority'
editType='dropdownedit'></e-column>
<e-column field='StartDate'
headerText='Start Date'
editType='datepickeredit'
format='yMd'></e-column>
<e-column field='IsCompleted'
headerText='Completed'
editType='booleanedit'></e-column>
</e-columns>
// ❌ WRONG - EditType mismatch
<e-column field='Duration'
headerText='Duration'
editType='TextBox'> <!-- Should be NumericTextBox -->
</e-column>Valid EditTypes:
| EditType | Best For | Validation |
|---|---|---|
| stringedit | String fields | max length |
| numericedit | Numbers | min, max, decimals |
| dropdownedit | Fixed options | required |
| datepickeredit | Dates | date range |
| booleanedit | Booleans | true/false |
| datetimepickeredit | Date and Time |
---
Rule 4: Primary Key (isPrimaryKey) is MANDATORY for CRUD Operations
Severity: 🔴 CRITICAL - Editing/Deleting fails silently without this
Requirement:
// ✅ REQUIRED - Exactly ONE column must have isPrimaryKey='true'
<e-columns>
<e-column field='TaskID' headerText='ID' isPrimaryKey='true'></e-column>
<e-column field='TaskName' headerText='Task'></e-column>
</e-columns>
// ❌ WRONG - No primary key = CRUD operations fail silently
<e-columns>
<e-column field='TaskID' headerText='ID'></e-column>
<e-column field='TaskName' headerText='Task'></e-column>
</e-columns>Rules:
- ✅ Only ONE primary key allowed per grid
- ✅ Primary key value must be UNIQUE for each row
- ✅ Primary key must not be NULL/undefined
- ✅ Must match a field in data source
- ❌ Do NOT mark multiple columns as primary key
- ❌ Do NOT use composite/multi-column primary keys
Impact Without This:
// What fails without isPrimaryKey:
- Edit operations (beginEdit fails silently)
- Delete operations (no row deleted)
- Update operations (data not persisted)
- Row selection preservation---
Editing Modes
Row Editing
import { Component } from '@angular/core';
import { EditService } from '@syncfusion/ej2-angular-treegrid';
@Component({
selector: 'app-treegrid',
template: `
<ejs-treegrid
[dataSource]='data'
[childMapping]='childMapping'
[editSettings]='editSettings'>
<e-columns>
<e-column field='TaskID' headerText='Task ID' width='90' isPrimaryKey='true'></e-column>
<e-column field='TaskName' headerText='Task Name' width='200'></e-column>
</e-columns>
</ejs-treegrid>
`,
providers: [EditService]
})
export class AppComponent {
public data: Object[] = [];
public childMapping: string = 'subtasks';
public editSettings = {
mode: 'Inline'
};
}Dialog Editing
public editSettings = {
mode: 'Dialog'
};Batch Editing
public editSettings = {
mode: 'Batch'
};Configure Editing
Enable CRUD Operations
public editSettings = {
mode: 'Inline',
allowEditing: true,
allowDeleting: true,
allowAdding: true
};Column Editor Type
<e-column field='Duration' headerText='Duration' type='number' editType='numericedit' width='100'></e-column>
<e-column field='StartDate' headerText='Start Date' type='date' editType='datepickeredit' width='130'></e-column>
<e-column field='Status' headerText='Status' editType='dropdownedit' width='100'></e-column>Validation
Field Validation
<e-column
field='TaskName'
headerText='Task Name'
width='200'
validationRules='{ required: true }'>
</e-column>
<e-column
field='Duration'
headerText='Duration'
type='number'
validationRules='{ required: true, min: 1, max: 100 }'>
</e-column>Events
Edit Events
<ejs-treegrid
(actionBegin)='onActionBegin($event)'
(actionComplete)='onActionComplete($event)'
(actionFailure)='onActionFailure($event)'>
<!-- columns -->
</ejs-treegrid>onActionBegin(args: any) {
if (args.requestType === 'save') {
console.log('Saving record:', args.data);
}
}
onActionComplete(args: any) {
if (args.requestType === 'save') {
console.log('Record saved successfully');
}
}
onActionFailure(args: any) {
console.error('Edit operation failed:', args);
}Optimistic Updates
Apply Changes Before Server Confirmation
import { Component, ViewChild } from '@angular/core';
import { TreeGridComponent, EditService } from '@syncfusion/ej2-angular-treegrid';
import { HttpClient } from '@angular/common/http';
@Component({
selector: 'app-treegrid',
template: `
<ejs-treegrid
#treegrid
[dataSource]='data'
[editSettings]='editSettings'
(beforeBatchSave)='onBeforeBatchSave($event)'
(actionFailure)='onActionFailure($event)'>
</ejs-treegrid>
`,
providers: [EditService]
})
export class AppComponent {
@ViewChild('treegrid') treegrid!: TreeGridComponent;
public data: Object[] = [];
public editSettings = { mode: 'Batch' };
private originalData: Object[] = [];
constructor(private http: HttpClient) {}
onBeforeBatchSave(args: any): void {
this.originalData = JSON.parse(JSON.stringify(this.data));
args.cancel = false;
this.saveChangesToServer(args.changedRecords);
}
private saveChangesToServer(changes: any[]): void {
this.http.post('/api/tasks/batch', changes).subscribe({
next: (response) => {
console.log('Changes saved successfully');
this.treegrid.refresh();
},
error: (error) => {
console.error('Save failed, reverting changes');
this.data = JSON.parse(JSON.stringify(this.originalData));
this.treegrid.refresh();
}
});
}
onActionFailure(args: any): void {
console.error('Operation failed:', args);
}
}TreeGrid Events - Table Reference
When to Use
Use events when you need to:
- Handle user actions — Respond to clicks, edits, selections
- Validate data — Execute validation logic before operations
- Cancel operations — Prevent unwanted actions (cancelable events)
- Async operations — Handle async work in event handlers
- Side effects — Trigger updates in other components
- Audit changes — Log user interactions and data changes
- Custom workflows — Implement business logic around events
Data Events
| Event | Type | Args | Cancelable | Description |
|---|---|---|---|---|
dataBound | EmitType<Object> | - | No | Fires when data source is populated |
beforeDataBound | EmitType<BeforeDataBoundArgs> | cancel, data | Yes | Fires before data binding |
dataSourceChanged | EmitType<DataSourceChangedEventArgs> | action, data, endEdit() | Yes | Fires on data add/edit/delete, requires endEdit() for async |
dataStateChange | EmitType<DataStateChangeEventArgs> | action, currentPage, pageSize | No | Fires on sort/page/filter complete |
Action Events
| Event | Type | Args | Cancelable | Description |
|---|---|---|---|---|
actionBegin | EmitType<ActionBeginEventArgs> | action, requestType, data | Yes | Fires when action starts (sort, filter, page, search, edit) |
actionComplete | EmitType<ActionCompleteEventArgs> | action, requestType, result | No | Fires when action completes |
actionFailure | EmitType<FailureEventArgs> | error | No | Fires when action fails |
Selection Events
| Event | Type | Args | Cancelable | Description |
|---|---|---|---|---|
rowSelecting | EmitType<RowSelectingEventArgs> | rowIndex, rowData, data | Yes | Fires before row selection |
rowSelected | EmitType<RowSelectEventArgs> | rowIndex, rowData, rowElement | No | Fires after row selection |
rowDeselecting | EmitType<RowDeselectEventArgs> | rowIndex, rowData, data | Yes | Fires before row deselection |
rowDeselected | EmitType<RowDeselectEventArgs> | rowIndex, rowData, data | No | Fires after row deselection |
cellSelecting | EmitType<CellSelectingEventArgs> | cellIndex, data | Yes | Fires before cell selection |
cellSelected | EmitType<CellSelectEventArgs> | cellIndex, data, cell | No | Fires after cell selection |
cellDeselecting | EmitType<CellDeselectEventArgs> | cellIndex, data | Yes | Fires before cell deselection |
cellDeselected | EmitType<CellDeselectEventArgs> | cellIndex, data | No | Fires after cell deselection |
checkboxChange | EmitType<CheckBoxChangeEventArgs> | rowIndex, rowData | Yes | Fires on checkbox toggle |
Expand/Collapse Events
| Event | Type | Args | Cancelable | Description |
|---|---|---|---|---|
expanding | EmitType<RowExpandingEventArgs> | rowIndex, rowData | Yes | Fires while row expanding |
expanded | EmitType<RowExpandedEventArgs> | rowIndex, rowData | No | Fires after row expanded |
collapsing | EmitType<RowCollapsingEventArgs> | rowIndex, rowData | Yes | Fires while row collapsing |
collapsed | EmitType<RowCollapsedEventArgs> | rowIndex, rowData | No | Fires after row collapsed |
Edit Events
| Event | Type | Args | Cancelable | Description |
|---|---|---|---|---|
beginEdit | EmitType<BeginEditEventArgs> | rowIndex, rowData, requestType | Yes | Fires before edit starts |
cellEdit | EmitType<CellEditEventArgs> | rowIndex, value, previousValue, column | Yes | Fires when editing cell |
cellSave | EmitType<CellSaveEventArgs> | rowIndex, value, previousValue, column | Yes | Fires when saving cell |
cellSaved | EmitType<CellSaveEventArgs> | rowIndex, value, column | No | Fires after cell saved |
beforeBatchAdd | EmitType<BeforeBatchAddArgs> | data | Yes | Fires before batch add |
batchAdd | EmitType<BatchAddArgs> | data | No | Fires on batch add |
beforeBatchDelete | EmitType<BeforeBatchDeleteArgs> | data | Yes | Fires before batch delete |
batchDelete | EmitType<BatchDeleteArgs> | data | No | Fires on batch delete |
beforeBatchSave | EmitType<BeforeBatchSaveArgs> | addedRecords, changedRecords, deletedRecords | Yes | Fires before batch save |
batchCancel | EmitType<BatchCancelArgs> | data | No | Fires on batch cancel |
Drag & Drop Events
| Event | Type | Args | Cancelable | Description |
|---|---|---|---|---|
rowDragStartHelper | EmitType<RowDragEventArgs> | rowIndex, rowData | Yes | Fires just before row drag |
rowDragStart | EmitType<RowDragEventArgs> | rowIndex, rowData, data | No | Fires when row drag starts |
rowDrag | EmitType<RowDragEventArgs> | rowIndex, rowData | No | Fires continuously during drag |
rowDrop | EmitType<RowDragEventArgs> | rowIndex, dropIndex, dropPosition | Yes | Fires when row dropped |
Menu Events
| Event | Type | Args | Cancelable | Description |
|---|---|---|---|---|
contextMenuOpen | EmitType<BeforeOpenCloseMenuEventArgs> | element, event | No | Fires before context menu opens |
contextMenuClick | EmitType<MenuEventArgs> | item, element | No | Fires on context menu item click |
columnMenuOpen | EmitType<ColumnMenuOpenEventArgs> | column | Yes | Fires before column menu opens |
columnMenuClick | EmitType<MenuEventArgs> | item, element | No | Fires on column menu item click |
toolbarClick | EmitType<ClickEventArgs> | item, originalEvent | Yes | Fires on toolbar item click |
Column Events
| Event | Type | Args | Cancelable | Description |
|---|---|---|---|---|
columnDragStart | EmitType<ColumnDragEventArgs> | column, columns | No | Fires when column drag starts |
columnDrag | EmitType<ColumnDragEventArgs> | column | No | Fires continuously during column drag |
columnDrop | EmitType<ColumnDragEventArgs> | column | No | Fires when column dropped |
resizeStart | EmitType<ResizeArgs> | column | Yes | Fires when column resize starts |
resizing | EmitType<ResizeArgs> | column | No | Fires during column resize |
resizeStop | EmitType<ResizeArgs> | column | No | Fires when column resize ends |
Export Events
| Event | Type | Args | Cancelable | Description |
|---|---|---|---|---|
beforeExcelExport | EmitType<Object> | - | Yes | Fires before Excel export |
excelQueryCellInfo | EmitType<ExcelQueryCellInfoEventArgs> | cell, data, column | No | Fires before each cell exports to Excel |
excelHeaderQueryCellInfo | EmitType<ExcelHeaderQueryCellInfoEventArgs> | cell, column | No | Fires before header cell exports to Excel |
excelAggregateQueryCellInfo | EmitType<AggregateQueryCellInfoEventArgs> | cell, column, aggregateType | No | Fires before aggregate cell exports to Excel |
excelExportComplete | EmitType<ExcelExportCompleteArgs> | promise | No | Fires after Excel export completes |
beforePdfExport | EmitType<Object> | - | Yes | Fires before PDF export |
pdfQueryCellInfo | EmitType<PdfQueryCellInfoEventArgs> | cell, data, column, style | No | Fires before each cell exports to PDF |
pdfHeaderQueryCellInfo | EmitType<PdfHeaderQueryCellInfoEventArgs> | cell, column | No | Fires before header cell exports to PDF |
pdfAggregateQueryCellInfo | EmitType<AggregateQueryCellInfoEventArgs> | cell, column, aggregateType | No | Fires before aggregate cell exports to PDF |
pdfExportComplete | EmitType<PdfExportCompleteArgs> | promise | No | Fires after PDF export completes |
Print Events
| Event | Type | Args | Cancelable | Description |
|---|---|---|---|---|
beforePrint | EmitType<PrintEventArgs> | - | Yes | Fires before print action |
printComplete | EmitType<PrintEventArgs> | - | No | Fires after print completes |
Query Events
| Event | Type | Args | Cancelable | Description |
|---|---|---|---|---|
queryCellInfo | EmitType<QueryCellInfoEventArgs> | data, cell, column, rowIndex, colIndex | No | Fires on cell access/info request |
rowDataBound | EmitType<RowDataBoundEventArgs> | data, row, rowIndex | No | Fires on row access/info request |
headerCellInfo | EmitType<HeaderCellInfoEventArgs> | cell, column | No | Fires on header access request |
detailDataBound | EmitType<DetailDataBoundEventArgs> | data, rowIndex | No | Fires after detail row expands |
recordDoubleClick | EmitType<RecordDoubleClickEventArgs> | data, rowIndex, column, cell | Yes | Fires on record double-click |
Copy/Paste Events
| Event | Type | Args | Cancelable | Description |
|---|---|---|---|---|
beforeCopy | EmitType<BeforeCopyEventArgs> | data | Yes | Fires before copy action |
beforePaste | EmitType<BeforePasteEventArgs> | data | Yes | Fires before paste action |
Lifecycle Events
| Event | Type | Args | Cancelable | Description |
|---|---|---|---|---|
load | EmitType<Object> | - | No | Fires before initial render, customize properties |
created | EmitType<Object> | - | No | Fires when component fully created |
Event Categories Summary
| Category | Count | Key Events |
|---|---|---|
| Data Events | 4 | dataBound, beforeDataBound, dataSourceChanged, dataStateChange |
| Action Events | 3 | actionBegin, actionComplete, actionFailure |
| Selection Events | 9 | rowSelecting, rowSelected, cellSelecting, checkboxChange |
| Expand/Collapse Events | 4 | expanding, expanded, collapsing, collapsed |
| Edit Events | 10 | beginEdit, cellEdit, cellSave, batch operations |
| Drag & Drop Events | 4 | rowDragStart, rowDrag, rowDrop |
| Menu Events | 5 | contextMenuClick, columnMenuClick, toolbarClick |
| Column Events | 6 | columnDragStart, columnDrop, resizeStart, resizing, resizeStop |
| Export Events | 10 | beforeExcelExport, excelQueryCellInfo, pdfExportComplete |
| Print Events | 2 | beforePrint, printComplete |
| Query Events | 5 | queryCellInfo, rowDataBound, headerCellInfo, recordDoubleClick |
| Copy/Paste Events | 2 | beforeCopy, beforePaste |
| Lifecycle Events | 2 | load, created |
| TOTAL | 56+ | All TreeGrid events |
Event Patterns
Cancelable Events (event.cancel = true)
rowSelecting, cellSelecting, rowDeselecting, cellDeselecting, checkboxChange,
expanding, collapsing, beginEdit, cellEdit, cellSave,
rowDragStartHelper, rowDrop,
columnMenuOpen, toolbarClick,
resizeStart,
beforeExcelExport, beforePdfExport, beforePrint,
recordDoubleClick, beforeCopy, beforePasteAsync Operations (require event.endEdit())
dataSourceChanged - when action is 'add' or 'edit'
Always call event.endEdit() even on errorExport Customization Events
Excel: excelQueryCellInfo (cell level), excelHeaderQueryCellInfo (header level)
PDF: pdfQueryCellInfo (cell level), pdfHeaderQueryCellInfo (header level)
Both: excelAggregateQueryCellInfo, pdfAggregateQueryCellInfo (aggregates)Excel Export
Export TreeGrid data to Excel with advanced formatting, merged cells, and conditional styling.
When to Use
Use Excel export when you need to:
- Generate reports — Export TreeGrid data as Excel files
- Apply styles — Maintain formatting in exported data
- Hierarchical export — Preserve parent-child relationships in Excel
- Custom headers — Add custom headers and footers to export
- Download files — Enable users to download data for offline use
Table of Contents
Enable Excel Export
Basic Excel Export
import { Component, ViewChild } from '@angular/core';
import { TreeGridComponent } from '@syncfusion/ej2-angular-treegrid';
import { ExcelExportService, ToolbarService } from '@syncfusion/ej2-angular-treegrid';
import { ClickEventArgs } from '@syncfusion/ej2-navigations';
@Component({
selector: 'app-treegrid',
template: `
<ejs-treegrid
#treegrid
[dataSource]='data'
[childMapping]='childMapping'
[allowExcelExport]='true'
[toolbar]='["ExcelExport"]'>
<e-columns>
<e-column field='TaskID' headerText='Task ID' width='90'></e-column>
<e-column field='TaskName' headerText='Task Name' width='200'></e-column>
</e-columns>
</ejs-treegrid>
`,
providers: [ExcelExportService, ToolbarService]
})
export class AppComponent {
@ViewChild('treegrid') treegrid!: TreeGridComponent;
public data: Object[] = [];
public childMapping: string = 'subtasks';
public toolbarClick(args: ClickEventArgs): void {
switch (args.item.id) {
case this.treegrid.grid.element.id + '_excelexport':
let excelExportProperties: TreeGridExcelExportProperties = {
isCollapsedStatePersist: this.collapseStatePersist
};
this.treegrid.excelExport(excelExportProperties);
break;
}
}
}Excel Export Options
Custom Filename
let excelExportProperties = {
fileName: 'TaskData.xlsx'
};
this.treegrid.excelExport(this.excelExportProperties);Export Current Page
public excelExportProperties = {
exportType: 'CurrentPage',
};Styling and Formatting
Column Width
public excelExportProperties = {
columns: [
{ field: 'TaskID', width: 100 },
{ field: 'TaskName', width: 250 }
]
};Cell Styling
import { Component, ViewChild } from '@angular/core';
import { TreeGridComponent, TreeGridModule, ExcelExportService, ToolbarService } from '@syncfusion/ej2-angular-treegrid';
import { ToolbarItems } from '@syncfusion/ej2-treegrid';
import { ExcelQueryCellInfoEventArgs, RowDataBoundEventArgs } from '@syncfusion/ej2-grids';
@Component({
standalone: true,
selector: 'app-container',
imports: [CommonModule, TreeGridModule],
providers: [PageService, ExcelExportService, ToolbarService],
template: `
<ejs-treegrid
#treegrid
[dataSource]="data"
[allowExcelExport]="true"
[toolbar]="toolbarOptions"
[treeColumnIndex]="1"
childMapping="subtasks"
(queryCellInfo)="onQueryCellInfo($event)"
(excelQueryCellInfo)="onExcelQueryCellInfo($event)"
(toolbarClick)="onToolbarClick($event)"
height="300">
<e-columns>
<e-column field="taskID" headerText="Task ID" textAlign="Right" width="90"></e-column>
<e-column field="taskName" headerText="Task Name" width="180"></e-column>
<e-column field="startDate" headerText="Start Date" format="yMd" textAlign="Right" width="120"></e-column>
<e-column field="duration" headerText="Duration" textAlign="Right" width="110"></e-column>
</e-columns>
</ejs-treegrid>
`
})
export class AppComponent {
@ViewChild('treegrid', { static: true }) treeGridObj!: TreeGridComponent;
public data = sampleData;
public toolbarOptions: ToolbarItems[] = ['ExcelExport'];
onToolbarClick(args: any): void {
if (args.item.id.includes('excelexport')) {
this.treeGridObj.excelExport();
}
}
onQueryCellInfo(args: RowDataBoundEventArgs & { column: any; cell: HTMLElement; data: any }): void {
if (args.column.field === 'duration') {
const value = args.data?.duration;
if (value === 0 || value === '') {
args.cell.style.background = '#336c12';
args.cell.style.color = '#ffffff';
} else if (value < 3) {
args.cell.style.background = '#7b2b1d';
args.cell.style.color = '#ffffff';
}
}
}
onExcelQueryCellInfo(args: ExcelQueryCellInfoEventArgs & { value: any }): void {
if (args.column?.field === 'duration') {
const value = args.value;
if (value === 0 || value === '') {
args.style = { backColor: '#336c12', fontColor: '#ffffff' };
} else if (value < 3) {
args.style = { backColor: '#7b2b1d', fontColor: '#ffffff' };
}
}
}
}Excel Export Events
<ejs-treegrid
(beforeExcelExport)='onBeforeExcelExport($event)'>
<!-- columns -->
</ejs-treegrid>onBeforeExcelExport(args: any) {
if (args.data) {
// Customize export data
console.log('Preparing Excel export');
}
}Filtering
Filtering enables users to find specific records based on column values using multiple filter modes.
When to Use
Use filtering when you need to:
- Enable data search — Allow users to find records matching specific criteria
- Filter by column values — Provide filter UI for individual columns
- Excel-like filtering — Show checkboxes for filtering by multiple values
- Custom filter expressions — Create complex filters combining multiple conditions
- Server-side filtering — Filter data on the backend for large datasets
- Dynamic filtering — Apply filters programmatically based on user interactions
- Hierarchical filtering — Filter parent and child records simultaneously
Table of Contents
Basic Filtering
Enable Filtering
import { Component } from '@angular/core';
import { FilterService } from '@syncfusion/ej2-angular-treegrid';
@Component({
selector: 'app-treegrid',
template: `
<ejs-treegrid
[dataSource]='data'
[childMapping]='childMapping'
[allowFiltering]='true'
[filterSettings]='filterSettings'>
<e-columns>
<e-column field='TaskID' headerText='Task ID' width='90' type='number'></e-column>
<e-column field='TaskName' headerText='Task Name' width='200'></e-column>
<e-column field='Status' headerText='Status' width='100'></e-column>
</e-columns>
</ejs-treegrid>
`,
providers: [FilterService]
})
export class AppComponent {
public data: Object[] = [];
public childMapping: string = 'subtasks';
public filterSettings = {
type: 'FilterBar'
};
}Filter Modes
Filter Bar
Users type in the filter row:
public filterSettings = {
type: 'FilterBar'
};Filter Menu
Dropdown with filter options:
public filterSettings = {
type: 'Menu'
};Excel-like Filter
Excel-style filtering interface:
public filterSettings = {
type: 'Excel'
};Checkbox Filter
Checkbox filtering interface:
public filterSettings = {
type: 'Checkbox'
};Custom Filters
Programmatic Filtering
this.treegrid.filterByColumn('Progress', 'greaterthan', 50);Clear Filters
this.treegrid.clearFiltering();Custom Filter Template
<ejs-treegrid
[dataSource]='data'
[childMapping]='childMapping'
[allowFiltering]='true'>
<e-columns>
<e-column field='Status'
headerText='Status'
width='120'>
<ng-template #filterTemplate let-data>
<select style="width: 100%;">
<option value="">All</option>
<option value="Completed">Completed</option>
<option value="InProgress">In Progress</option>
<option value="Pending">Pending</option>
</select>
</ng-template>
</e-column>
</e-columns>
</ejs-treegrid>
Predicate-based Filtering
import { Predicate } from '@syncfusion/ej2-data';
filterWithPredicates() {
const predicate1 = new Predicate('Status', 'equal', 'Completed');
const predicate2 = new Predicate('Priority', 'equal', 'High');
const finalPredicate = predicate1.or(predicate2);
this.treegrid.filterByColumn(finalPredicate);
}Date Range Filter
let startDate: string = new Date('2026-01-01');
let endDate: string = new Date('2026-01-05');
const predicate1 = new Predicate('TaskDate', 'greaterthanorequal', this.startDate);
const predicate2 = new Predicate('TaskDate', 'lessthanorequal', this.endDate);
const finalPredicate = predicate1.and(predicate2);
this.treegrid.filterByColumn(finalPredicate);
Filter Events
Before Filter
<ejs-treegrid
(actionBegin)='onActionBegin($event)'>
<!-- columns -->
</ejs-treegrid>onActionBegin(args: any) {
if (args.requestType === 'filtering') {
console.log('Filtering started:', args.filterModel);
}
}After Filter
<ejs-treegrid
(actionComplete)='onActionComplete($event)'>
<!-- columns -->
</ejs-treegrid>onActionComplete(args: any) {
if (args.requestType === 'filtering') {
console.log('Records Filtered');
}
}Prevent Filter
onActionBegin(args: any) {
if (args.requestType === 'filtering' && args.column?.field === 'SecureField') {
args.cancel = true; // Prevent filtering on secure field
}
}Foreign Key Columns
Foreign Key columns enable relationships between TreeGrid data and external lookup tables for dropdown selection and display mapping.
When to Use
Use Foreign Key columns when you need to:
- Lookup values — Map IDs to display text from related tables
- Dropdown editing — Show dropdown lists in edit mode
- Data relationships — Establish connections between related data
Table of Contents
Foreign Key Configuration
Basic Foreign Key Setup
import { Component } from '@angular/core';
import { DataManager, UrlAdaptor, ForeignKeyService, EditService } from '@syncfusion/ej2-data';
@Component({
selector: 'app-treegrid',
template: `
<ejs-treegrid
[dataSource]='data'
[childMapping]='childMapping'
[editSettings]='editSettings'>
<e-columns>
<e-column
field='TaskID'
headerText='Task ID'
width='90'
isPrimaryKey='true'>
</e-column>
<e-column
field='TaskName'
headerText='Task Name'
width='200'>
</e-column>
<e-column
field='AssignedTo'
headerText='Assigned To'
foreignKeyField='EmployeeID'
foreignKeyValue='EmployeeName'
dataSource='employeeData'
editType='dropdownlist'
width='150'>
</e-column>
<e-column
field='DepartmentID'
headerText='Department'
foreignKeyField='DeptID'
foreignKeyValue='DeptName'
dataSource='departmentData'
editType='dropdownlist'
width='120'>
</e-column>
</e-columns>
</ejs-treegrid>
`,
providers: [ForeignKeyService, EditService]
})
export class AppComponent {
public data: Object[] = [
{
TaskID: 1,
TaskName: 'Project Planning',
AssignedTo: 1, // Foreign Key (ID)
DepartmentID: 10, // Foreign Key (ID)
subtasks: [
{ TaskID: 2, TaskName: 'Scope', AssignedTo: 2, DepartmentID: 10 }
]
}
];
// Lookup data for Employee FK
public employeeData = [
{ EmployeeID: 1, EmployeeName: 'Alice Johnson' },
{ EmployeeID: 2, EmployeeName: 'Bob Smith' },
{ EmployeeID: 3, EmployeeName: 'Carol Williams' }
];
// Lookup data for Department FK
public departmentData = [
{ DeptID: 10, DeptName: 'Engineering' },
{ DeptID: 20, DeptName: 'Marketing' },
{ DeptID: 30, DeptName: 'Sales' }
];
public childMapping: string = 'subtasks';
public editSettings = {
mode: 'Dialog',
allowEditing: true,
allowAdding: true,
allowDeleting: true
};
}---
Remote Data Binding
Load FK Data from Server
import { DataManager, UrlAdaptor, ODataV4Adaptor } from '@syncfusion/ej2-data';
@Component({
selector: 'app-treegrid',
template: `
<ejs-treegrid
[dataSource]='taskDataManager'
[childMapping]='childMapping'
[editSettings]='editSettings'
idMapping='TaskID'
parentIdMapping='ParentID'>
<e-columns>
<e-column
field='TaskID'
headerText='Task ID'
width='90'
isPrimaryKey='true'>
</e-column>
<e-column
field='TaskName'
headerText='Task Name'
width='200'>
</e-column>
<e-column
field='AssignedToID'
headerText='Assigned To'
foreignKeyField='EmployeeID'
foreignKeyValue='EmployeeName'
[dataSource]='employeeDataManager'
editType='dropdownlist'
width='150'>
</e-column>
</e-columns>
</ejs-treegrid>
`,
providers: [EditService]
})
export class AppComponent {
// Remote data for TreeGrid
public taskDataManager = new DataManager({
url: 'url',
adaptor: new ODataV4Adaptor(),
crossDomain: true
});
// Remote data for FK lookup
public employeeDataManager = new DataManager({
url: 'url',
adaptor: new ODataV4Adaptor(),
crossDomain: true,
pageSize: 20
});
public data: Object[] = [];
public childMapping: string = 'Children';
public editSettings = {
mode: 'Dialog',
allowEditing: true,
allowAdding: true,
allowDeleting: true
};
}Frozen Columns & Rows
Freezing columns and rows keeps them visible while scrolling through data.
When to Use
Use frozen columns/rows when you need to:
- Keep headers visible — Always show column headers while scrolling horizontally
- Lock key columns — Keep ID or name column visible while viewing other data
- Freeze row numbers — Display row index or row numbers while scrolling
- Fix action columns — Keep command/action columns visible during horizontal scroll
- Improve readability — Maintain context while viewing distant columns
- Multi-level headers — Freeze parent headers in grouped columns
- Large datasets — Enhance UX when viewing wide tables with many columns
Table of Contents
Freeze Columns
Basic Column Freezing
import { Component } from '@angular/core';
@Component({
selector: 'app-treegrid',
template: `
<ejs-treegrid
[dataSource]='data'
[childMapping]='childMapping'
width='100%'>
<e-columns>
<e-column field='TaskID' headerText='Task ID' width='90' isFrozen='true'></e-column>
<e-column field='TaskName' headerText='Task Name' width='200' isFrozen='true'></e-column>
<e-column field='Duration' headerText='Duration' width='100'></e-column>
<e-column field='Progress' headerText='Progress' width='100'></e-column>
</e-columns>
</ejs-treegrid>
`
})
export class AppComponent {
public data: Object[] = [];
public childMapping: string = 'subtasks';
}Freeze Column Index
<ejs-treegrid
[dataSource]='data'
[childMapping]='childMapping'
[frozenColumns]='2'
width='100%'>
<e-columns>
<!-- First 2 columns will be frozen -->
<e-column field='TaskID' headerText='Task ID' width='90'></e-column>
<e-column field='TaskName' headerText='Task Name' width='200'></e-column>
<e-column field='Duration' headerText='Duration' width='100'></e-column>
</e-columns>
</ejs-treegrid>Freeze Rows
Basic Row Freezing
<ejs-treegrid
[dataSource]='data'
[childMapping]='childMapping'
[frozenRows]='1'
width='100%'>
<e-columns>
<e-column field='TaskID' headerText='Task ID' width='90'></e-column>
<e-column field='TaskName' headerText='Task Name' width='200'></e-column>
</e-columns>
</ejs-treegrid>// First row will remain visible when scrolling vertically
public frozenRows = 1;Configuration
Freeze Column and Row Together
<ejs-treegrid
[dataSource]='data'
[childMapping]='childMapping'
[frozenColumns]='2'
[frozenRows]='1'
height='500px'
width='100%'>
<e-columns>
<e-column field='TaskID' headerText='Task ID' width='90'></e-column>
<e-column field='TaskName' headerText='Task Name' width='200'></e-column>
<e-column field='Duration' headerText='Duration' width='100'></e-column>
<e-column field='Progress' headerText='Progress' width='100'></e-column>
</e-columns>
</ejs-treegrid>Dynamic Freezing
<button (click)='freezeColumn("TaskName")'>Freeze Task Name</button>
<button (click)='unfreezeAll()'>Unfreeze All</button>
<ejs-treegrid
#treegrid
[dataSource]='data'
[childMapping]='childMapping'
width='100%'>
<!-- columns -->
</ejs-treegrid>@ViewChild('treegrid') treegrid!: TreeGridComponent;
freezeColumn(columnField: string) {
const column = this.treegrid.getColumnByField(columnField);
if (column) {
column.isFrozen = true;
this.treegrid.refresh();
}
}
unfreezeAll() {
this.treegrid.getColumns().forEach(col => col.isFrozen = false);
this.treegrid.refresh();
}Freeze with Aggregates
<ejs-treegrid
[dataSource]='data'
[childMapping]='childMapping'
[frozenColumns]='1'
[aggregates]='aggregates'>
<e-columns>
<e-column field='TaskID' headerText='Task ID' width='90' isFrozen='true'></e-column>
<e-column field='TaskName' headerText='Task Name' width='200'></e-column>
<e-column field='Amount' headerText='Amount' width='100' type='number'></e-column>
</e-columns>
</ejs-treegrid>public aggregates = [
{
properties: ['Amount'],
columnName: 'Amount',
type: 'Sum',
footerTemplate: 'Total: ${Sum}'
}
];Performance
Optimize Frozen Columns
// Good: Freeze only essential columns
<e-column field='ID' isFrozen='true'></e-column>
<e-column field='Name' isFrozen='true'></e-column>
// Avoid: Freezing too many columns impacts performance
// <e-column field='Col1' isFrozen='true'></e-column>
// <e-column field='Col2' isFrozen='true'></e-column>
// ... many more frozen columnsWidth Configuration
// Must specify explicit widths for frozen columns
<e-column field='TaskID' headerText='Task ID' width='90' isFrozen='true'></e-column>
<e-column field='TaskName' headerText='Task Name' width='150' isFrozen='true'></e-column>
<!-- Avoid flexible widths on frozen columns -->
<!-- <e-column field='Task' width='auto' isFrozen='true'></e-column> -->Virtual Scrolling with Frozen Columns
<ejs-treegrid
[dataSource]='dataManager'
[childMapping]='childMapping'
[enableVirtualization]='true'
[frozenColumns]='1'
[pageSettings]='{ pageSize: 100 }'>
<e-columns>
<e-column field='TaskID' isFrozen='true' width='90'></e-column>
<e-column field='TaskName' width='200'></e-column>
</e-columns>
</ejs-treegrid>Limitations
- Cannot use auto width for frozen columns
- Limited styling options for frozen area
- Reordering may not work as expected with frozen columns
- Print/export may not maintain frozen column layout
Best practices:
- Use freezing for key identifier columns only
- Test performance with actual data volume
- Consider using rowTemplate for complex layouts
- Disable freezing for mobile devices
Getting Started Guide
Quick start guide to set up Syncfusion Angular TreeGrid in your Angular project.
When to Use
Use this guide when you need to:
- New project setup — Install TreeGrid from scratch
- Learn basics — Understand core TreeGrid concepts
- First component — Create your first TreeGrid
- Module configuration — Set up required imports and providers
- Data binding — Connect data sources
- Feature enabled — Enable paging, sorting, filtering
- Quick examples — See working code examples
Table of Contents
Installation
Step 1: Install Syncfusion Packages
npm install @syncfusion/ej2-angular-treegrid --save
npm install @syncfusion/ej2-base --save
npm install @syncfusion/ej2-Angular-grids --saveStep 2: Include CSS
Add CSS files to your styles.css or angular.json:
/* styles.css */
@import "@syncfusion/ej2-base/styles/material.css";
@import "@syncfusion/ej2-buttons/styles/material.css";
@import "@syncfusion/ej2-calendars/styles/material.css";
@import "@syncfusion/ej2-dropdowns/styles/material.css";
@import "@syncfusion/ej2-inputs/styles/material.css";
@import "@syncfusion/ej2-popups/styles/material.css";
@import "@syncfusion/ej2-grids/styles/material.css";
@import "@syncfusion/ej2-angular-treegrid/styles/material.css";Or update angular.json:
{
"projects": {
"your-app": {
"architect": {
"build": {
"options": {
"styles": [
"@syncfusion/ej2-base/styles/material.css",
"@syncfusion/ej2-buttons/styles/material.css",
"@syncfusion/ej2-grids/styles/material.css",
"@syncfusion/ej2-angular-treegrid/styles/material.css"
]
}
}
}
}
}
}Step 3: Register License Key (Optional but Recommended)
For licensed versions, register your license key in main.ts:
import { registerLicense } from '@syncfusion/ej2-base';
registerLicense('YOUR_SYNCFUSION_LICENSE_KEY');
bootstrapApplication(AppComponent);Module Setup
Import TreeGrid Module
import { Component } from '@angular/core';
import { TreeGridModule } from '@syncfusion/ej2-angular-treegrid';
@Component({
selector: 'app-root',
standalone: true,
imports: [TreeGridModule],
template: `
<ejs-treegrid [dataSource]='data'>
<e-columns>
<e-column field='TaskID' headerText='Task ID' width='90'></e-column>
<e-column field='TaskName' headerText='Task Name' width='150'></e-column>
</e-columns>
</ejs-treegrid>
`
})
export class AppComponent {
public data: Object[] = [];
}Import in NgModule (Traditional)
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { TreeGridModule } from '@syncfusion/ej2-angular-treegrid';
@NgModule({
declarations: [AppComponent],
imports: [BrowserModule, TreeGridModule],
bootstrap: [AppComponent]
})
export class AppModule { }Basic TreeGrid Component
Enable Common Features
import { Component } from '@angular/core';
@Component({
selector: 'app-treegrid',
template: `
<ejs-treegrid
[dataSource]='data'
[childMapping]='childMapping'
[allowPaging]='true'
[allowSorting]='true'
[allowFiltering]='true'
[pageSettings]='pageSettings'
[editSettings]='editSettings'>
<e-columns>
<e-column field='TaskID' headerText='Task ID' width='90' isPrimaryKey='true'></e-column>
<e-column field='TaskName' headerText='Task Name' width='150'></e-column>
<e-column field='Duration' headerText='Duration' width='100'></e-column>
<e-column field='Progress' headerText='Progress' width='100'></e-column>
</e-columns>
</ejs-treegrid>
`
})
export class AppComponent {
public childMapping: string = 'subtasks';
public pageSettings = {
pageSize: 20
};
public editSettings = {
allowEditing: true,
allowAdding: true,
allowDeleting: true,
mode: 'Inline'
};
public data: Object[] = [
// ... data
];
}Inject Required Services
For advanced features, inject required services:
import { Component } from '@angular/core';
import {
PageService,
SortService,
FilterService,
EditService,
ExcelExportService,
PdfExportService,
PrintService
} from '@syncfusion/ej2-angular-treegrid';
@Component({
selector: 'app-treegrid',
template: `...`,
providers: [
PageService,
SortService,
FilterService,
EditService,
ExcelExportService,
PdfExportService,
PrintService
]
})
export class AppComponent {
// ...
}Data Binding Basics
Local Data Array
public data: Object[] = [
{ TaskID: 1, TaskName: 'Task 1', Duration: 8 },
{ TaskID: 2, TaskName: 'Task 2', Duration: 10 }
];Hierarchical Data with Child Mapping
public data: Object[] = [
{
TaskID: 1,
TaskName: 'Parent Task',
Duration: 8,
subtasks: [
{ TaskID: 2, TaskName: 'Child Task 1', Duration: 4 },
{ TaskID: 3, TaskName: 'Child Task 2', Duration: 4 }
]
}
];
public childMapping: string = 'subtasks';Remote Data with DataManager
import { DataManager, UrlAdaptor } from '@syncfusion/ej2-data';
@Component({
template: `
<ejs-treegrid
[dataSource]='dataManager'
[childMapping]='childMapping'>
</ejs-treegrid>
`
})
export class AppComponent {
public childMapping: string = 'subtasks';
public dataManager = new DataManager({
url: 'url',
adaptor: new UrlAdaptor()
});
}Dynamic Data Loading
import { Component, OnInit } from '@angular/core';
import { HttpClient } from '@angular/common/http';
@Component({
selector: 'app-treegrid',
template: `
<div *ngIf='loading'>Loading...</div>
<ejs-treegrid *ngIf='!loading' [dataSource]='data'>
<!-- columns -->
</ejs-treegrid>
`
})
export class AppComponent implements OnInit {
public data: Object[] = [];
public loading: boolean = true;
constructor(private http: HttpClient) {}
ngOnInit() {
this.loadData();
}
private loadData(): void {
this.http.get('url').subscribe({
next: (response: any) => {
this.data = response;
this.loading = false;
},
error: (error) => {
console.error('Failed to load data:', error);
this.loading = false;
}
});
}
}Quick Reference
| Feature | Enable |
|---|---|
| Paging | [allowPaging]='true' |
| Sorting | [allowSorting]='true' |
| Filtering | [allowFiltering]='true' |
| Editing | [editSettings]='editSettings' |
| Selection | Built-in by default |
| Virtualization | [enableVirtualization]='true' |
| Export | Inject ExcelExportService, PdfExportService |
Globalization
Globalization provides localization, RTL support, and culture-specific formatting for international use.
When to Use
Use globalization features when you need to:
- Multi-language support — Display UI in different languages
- RTL languages — Support Arabic, Hebrew, Persian
- Culture-specific formatting — Format dates, numbers by locale
- International apps — Adapt for global audience
- Custom translations — Add domain-specific language packs
- Date/time formatting — Show dates in user's locale format
- Currency display — Show currency symbols and formatting by region
Table of Contents
Locale Configuration
Set Locale
import { Component } from '@angular/core';
import { L10n } from '@syncfusion/ej2-base';
@Component({
selector: 'app-treegrid',
template: `
<ejs-treegrid
[dataSource]='data'
[childMapping]='childMapping'
locale='fa-AF'>
<e-columns>
<e-column field='TaskID' headerText='Task ID' width='90'></e-column>
<e-column field='TaskName' headerText='Task Name' width='200'></e-column>
</e-columns>
</ejs-treegrid>
`
})
export class AppComponent {
constructor() {
L10n.load({
'fa-AF': {
'treegrid': {
'Add': 'اضافہ',
'Delete': 'حذف',
'Edit': 'ترمیم'
}
}
});
}
public data: Object[] = [];
public childMapping: string = 'subtasks';
}RTL Support
Enable RTL
<ejs-treegrid
[dataSource]='data'
[childMapping]='childMapping'
[enableRtl]='true'>
<e-columns>
<e-column field='TaskID' headerText='Task ID' width='90'></e-column>
<e-column field='TaskName' headerText='Task Name' width='200'></e-column>
</e-columns>
</ejs-treegrid>Custom Localization
Define Custom Translations
L10n.load({
'en': {
'treegrid': {
'Add': 'New Task',
'Edit': 'Modify',
'Delete': 'Remove',
'Save': 'Apply',
'Cancel': 'Discard',
'Search': 'Find Tasks'
}
}
});Regional Date Format
<e-column
field='StartDate'
headerText='Start Date'
type='date'
format='long'>
</e-column>Printing
Printing allows users to generate print-friendly representations of TreeGrid data.
When to Use
Use printing when you need to:
- Print TreeGrid — Enable users to print grid data
- Print preview — Show how data will appear when printed
- Page formatting — Control page breaks and layout
- Custom print templates — Create specialized print layouts
- Header/footer — Add titles and information to printed pages
- Quick browser printing — Use native browser print functionality
Enable Printing
Basic Print
import { Component, ViewChild } from '@angular/core';
import { TreeGridComponent } from '@syncfusion/ej2-angular-treegrid';
import { ToolbarService } from '@syncfusion/ej2-angular-treegrid';
@Component({
selector: 'app-treegrid',
template: `
<ejs-treegrid
#treegrid
[dataSource]='data'
[childMapping]='childMapping'
[toolbar]='toolbarItems'>
<e-columns>
<e-column field='TaskID' headerText='Task ID' width='90'></e-column>
<e-column field='TaskName' headerText='Task Name' width='200'></e-column>
</e-columns>
</ejs-treegrid>
`,
providers: [ToolbarService]
})
export class AppComponent {
@ViewChild('treegrid') treegrid!: TreeGridComponent;
public data: Object[] = [];
public childMapping: string = 'subtasks';
public toolbarItems = ['Print'];
}