Organizational Chart Layout in Angular Diagram Control
23 Aug 202524 minutes to read
An organizational chart is a diagram that displays the hierarchical structure of an organization, showing reporting relationships and roles within the company. The Angular Diagram component provides specialized support for creating professional organizational charts through automatic layout algorithms. To create an organizational chart, set the type
property of the layout to OrganizationalChart
.
Prerequisites
Before implementing organizational charts, ensure the following modules are injected:
- HierarchicalTree module - Required for organizational chart layout functionality
- DataBinding module - Required when using DataSource approach
Creating Organizational Charts
The Angular Diagram component supports two primary approaches for creating organizational charts:
- DataSource approach - Recommended for dynamic data scenarios
- Manual approach - Using predefined nodes and connectors for static structures
Organizational Chart with DataSource
This approach is ideal when working with dynamic data from databases, APIs, or when the organizational structure changes frequently. The component automatically generates nodes and connectors based on the provided data structure.
import { Component, ViewEncapsulation, ViewChild } from '@angular/core';
import { DiagramComponent, Diagram, NodeModel, ConnectorModel, LayoutModel, DataSourceModel, DiagramModule,
HierarchicalTreeService, DataBindingService, DataBinding, HierarchicalTree } from '@syncfusion/ej2-angular-diagrams';
import { DataManager, Query } from '@syncfusion/ej2-data';
Diagram.Inject(DataBinding, HierarchicalTree);
@Component({
imports: [ DiagramModule ],
providers: [HierarchicalTreeService, DataBindingService],
standalone: true,
selector: "app-container",
template: `<ejs-diagram #diagram id="diagram" width="100%" height="580px" [getNodeDefaults]="getNodeDefaults"
[getConnectorDefaults]="getConnectorDefaults" [layout]="layout" [dataSourceSettings]="dataSourceSettings">
</ejs-diagram>`,
encapsulation: ViewEncapsulation.None
})
export class AppComponent {
@ViewChild("diagram")
public diagram?: DiagramComponent;
public items?: DataManager;
public layout?: LayoutModel;
public dataSourceSettings?: DataSourceModel;
//Initializes data source
public data: object[] = [
{
Id: 'parent',
Role: 'Project Management',
},
{
Id: 1,
Role: 'R&D Team',
Team: 'parent',
},
{
Id: 3,
Role: 'Philosophy',
Team: '1',
},
{
Id: 4,
Role: 'Organization',
Team: '1',
},
{
Id: 5,
Role: 'Technology',
Team: '1',
},
{
Id: 7,
Role: 'Funding',
Team: '1',
},
{
Id: 8,
Role: 'Resource Allocation',
Team: '1',
},
{
Id: 9,
Role: 'Targeting',
Team: '1',
},
{
Id: 11,
Role: 'Evaluation',
Team: '1',
},
{
Id: 156,
Role: 'HR Team',
Team: 'parent',
},
{
Id: 13,
Role: 'Recruitment',
Team: '156',
},
{
Id: 112,
Role: 'Employee Relation',
Team: '156',
},
{
Id: 17,
Role: 'Production & Sales Team',
Team: 'parent',
},
{
Id: 119,
Role: 'Design',
Team: '17',
},
{
Id: 19,
Role: 'Operation',
Team: '17',
},
{
Id: 20,
Role: 'Support',
Team: '17',
},
{
Id: 21,
Role: 'Quality Assurance',
Team: '17',
},
{
Id: 23,
Role: 'Customer Interaction',
Team: '17',
},
{
Id: 24,
Role: 'Support and Maintenance',
Team: '17',
},
{
Id: 25,
Role: 'Task Coordination',
Team: '17',
}
];
//Sets the default properties for all the Nodes
public getNodeDefaults(node: NodeModel): NodeModel {
node.annotations = [{ content: (node.data as { Role: 'string' }).Role }];
node.width = 75;
node.height = 40;
return node;
}
//Sets the default properties for all the connectors
public getConnectorDefaults(connector: ConnectorModel): ConnectorModel {
connector.type = 'Orthogonal';
return connector;
}
ngOnInit(): void {
this.items = new DataManager(this.data as JSON[], new Query().take(5));
//Uses layout to auto-arrange nodes on the Diagram page
this.layout = {
//set layout type
type: 'OrganizationalChart'
}
//Configures data source for Diagram
this.dataSourceSettings = {
id: 'Id',
parentId: 'Team',
dataSource: this.items
}
}
}
import { bootstrapApplication } from '@angular/platform-browser';
import { AppComponent } from './app.component';
import 'zone.js';
bootstrapApplication(AppComponent).catch((err) => console.error(err));
Note: When using organizational chart layout, both HierarchicalTree and DataBinding modules must be injected into the diagram component.
Organizational Chart with Nodes and Connectors
This manual approach provides complete control over the chart structure and is suitable for static organizational hierarchies or when custom node designs are required.
import { Component, ViewEncapsulation, ViewChild } from '@angular/core';
import { DiagramComponent, Diagram, NodeModel, ConnectorModel, LayoutModel, HierarchicalTree, DiagramModule,
HierarchicalTreeService, DataBindingService, DataBinding } from '@syncfusion/ej2-angular-diagrams';
Diagram.Inject(DataBinding, HierarchicalTree);
@Component({
imports: [ DiagramModule ],
providers: [HierarchicalTreeService, DataBindingService],
standalone: true,
selector: "app-container",
template: `<ejs-diagram #diagram id="diagram" width="100%" height="580px" [nodes]="nodes" [connectors]="connectors"
[getNodeDefaults]="getNodeDefaults" [getConnectorDefaults]="getConnectorDefaults" [layout]="layout"> </ejs-diagram>`,
encapsulation: ViewEncapsulation.None
})
export class AppComponent {
@ViewChild("diagram")
public diagram?: DiagramComponent;
public layout?: LayoutModel;
//Initialize nodes for diagram
public nodes: NodeModel[] = [
{ id: 'Project Management' },
{ id: 'R&D Team' },
{ id: 'Philosophy' },
{ id: 'Organization' },
{ id: 'Technology' },
{ id: 'Funding' },
{ id: 'Resource Allocation' },
{ id: 'Targeting' },
{ id: 'Evaluation' },
{ id: 'HR Team' },
{ id: 'Recruitment' },
{ id: 'Employee Relation' },
{ id: 'Production & Sales Team' },
{ id: 'Design' },
{ id: 'Operation' },
{ id: 'Support' },
{ id: 'Quality Assurance' },
{ id: 'Customer Interaction' },
{ id: 'Support and Maintenance' },
{ id: 'Task Coordination' },
];
//Initialize connectors for diagram
public connectors: ConnectorModel[] = [
{
id: 'Project Management-R&D Team',
sourceID: 'Project Management',
targetID: 'R&D Team',
},
{
id: 'R&D Team-Philosophy',
sourceID: 'R&D Team',
targetID: 'Philosophy'
},
{
id: 'R&D Team-Organization',
sourceID: 'R&D Team',
targetID: 'Organization',
},
{
id: 'R&D Team-Technology',
sourceID: 'R&D Team',
targetID: 'Technology'
},
{
id: 'R&D Team-Funding',
sourceID: 'R&D Team',
targetID: 'Funding'
},
{
id: 'R&D Team-Resource Allocation',
sourceID: 'R&D Team',
targetID: 'Resource Allocation',
},
{
id: 'R&D Team-Targeting',
sourceID: 'R&D Team',
targetID: 'Targeting'
},
{
id: 'R&D Team-Evaluation',
sourceID: 'R&D Team',
targetID: 'Evaluation'
},
{
id: 'Project Management-HR Team',
sourceID: 'Project Management',
targetID: 'HR Team',
},
{
id: 'HR Team-Recruitment',
sourceID: 'HR Team',
targetID: 'Recruitment'
},
{
id: 'HR Team-Employee Relation',
sourceID: 'HR Team',
targetID: 'Employee Relation',
},
{
id: 'Project Management-Production & Sales Team',
sourceID: 'Project Management',
targetID: 'Production & Sales Team',
},
{
id: 'Production & Sales Team-Design',
sourceID: 'Production & Sales Team',
targetID: 'Design',
},
{
id: 'Production & Sales Team-Operation',
sourceID: 'Production & Sales Team',
targetID: 'Operation',
},
{
id: 'Production & Sales Team-Support',
sourceID: 'Production & Sales Team',
targetID: 'Support',
},
{
id: 'Production & Sales Team-Quality Assurance',
sourceID: 'Production & Sales Team',
targetID: 'Quality Assurance',
},
{
id: 'Production & Sales Team-Customer Interaction',
sourceID: 'Production & Sales Team',
targetID: 'Customer Interaction',
},
{
id: 'Production & Sales Team-Support and Maintenance',
sourceID: 'Production & Sales Team',
targetID: 'Support and Maintenance',
},
{
id: 'Production & Sales Team-Task Coordination',
sourceID: 'Production & Sales Team',
targetID: 'Task Coordination',
},
];
//Sets the default properties for all the Nodes
public getNodeDefaults(node: NodeModel): NodeModel {
node.annotations = [{ content: node.id }];
node.width = 75;
node.height = 40;
return node;
}
//Sets the default properties for all the connectors
public getConnectorDefaults(connector: ConnectorModel): ConnectorModel {
connector.type = 'Orthogonal';
return connector;
}
ngOnInit(): void {
//Uses layout to auto-arrange nodes on the Diagram page
this.layout = {
//set layout type
type: 'OrganizationalChart'
}
}
}
import { bootstrapApplication } from '@angular/platform-browser';
import { AppComponent } from './app.component';
import 'zone.js';
bootstrapApplication(AppComponent).catch((err) => console.error(err));
Advanced Layout Customization with getLayoutInfo
The getLayoutInfo
method provides granular control over how each subtree within the organizational chart is arranged. This method is invoked for every node during the layout process, allowing customization of orientation, alignment, spacing, and special node types like assistants.
The organizational chart layout engine parses the hierarchy starting from the root node and processes each subtree. By overriding the getLayoutInfo
method, developers can customize the arrangement of child nodes based on specific business requirements.
getLayoutInfo Parameters
The getLayoutInfo
method accepts the following parameters:
- node: The parent node for which layout options are being configured
- options: Configuration object containing customizable layout properties
Layout Options Properties
Property | Description | Default Value |
---|---|---|
options.assistants | Collection of child nodes to be treated as assistants with special positioning | Empty array |
options.orientation | Controls the primary direction of child node arrangement | SubTreeOrientation.Vertical |
options.type | Defines how child nodes are aligned relative to the parent | Horizontal: SubTreeAlignments.Center, Vertical: SubTreeAlignments.Alternate |
options.offset | Horizontal spacing between parent and child nodes | 20 pixels (vertical orientations only) |
options.hasSubTree | Indicates whether the current node contains child subtrees | Boolean |
options.level | Represents the depth level of the node from the root | Number |
options.enableRouting | Controls whether automatic connector routing is applied | true |
options.rows | Number of rows for arranging child nodes (balanced type only) | Number |
Orientation and Alignment Options
The following table describes the available chart orientations and their corresponding alignment types:
Orientation | Type | Description | Example |
---|---|---|---|
Horizontal | Left | Positions child nodes horizontally to the left of the parent | |
Right | Positions child nodes horizontally to the right of the parent | ||
Center | Arranges children in standard tree layout format | ||
Balanced | Distributes leaf-level nodes across multiple rows for better space utilization | ||
Vertical | Left | Arranges children vertically on the left side of the parent | |
Right | Arranges children vertically on the right side of the parent | ||
Alternate | Distributes children on both left and right sides of the parent |
Horizontal Subtree Orientation Example
The following example demonstrates customizing subtree alignment for horizontal organizational structures:
import { Component, ViewEncapsulation, ViewChild } from '@angular/core';
import { DiagramComponent, Diagram, NodeModel, ConnectorModel, SnapSettingsModel, LayoutModel, DataSourceModel, DiagramModule, DecoratorModel,
HierarchicalTreeService, DataBindingService, TreeInfo, DataBinding, HierarchicalTree, SubTreeAlignments } from '@syncfusion/ej2-angular-diagrams';
import { DataManager, Query } from '@syncfusion/ej2-data';
Diagram.Inject(DataBinding, HierarchicalTree);
@Component({
imports: [ DiagramModule ],
providers: [HierarchicalTreeService, DataBindingService],
standalone: true,
selector: "app-container",
template: `
<label for="alignment">Alignment: </label>
<select id="alignment" #alignment (change)="onAlignmentChange($event)">
<option value="Center">Center</option>
<option value="Right">Right</option>
<option value="Left">Left</option>
<option value="Balanced">Balanced</option>
</select>
<ejs-diagram #diagram id="diagram" width="100%" height="530px" [getNodeDefaults]="getNodeDefaults" [getConnectorDefaults]="getConnectorDefaults"
[snapSettings]="snapSettings" [layout]="layout" [dataSourceSettings]="dataSourceSettings"> </ejs-diagram>`,
encapsulation: ViewEncapsulation.None
})
export class AppComponent {
@ViewChild("diagram")
public diagram!: DiagramComponent;
public snapSettings?: SnapSettingsModel;
public items?: DataManager;
public layout?: LayoutModel;
public dataSourceSettings?: DataSourceModel;
//Initializes data source
public data: object[] = [
{ Id: 1, Role: 'General Manager' },
{ Id: 2, Role: 'Assistant Manager', Team: 1 },
{ Id: 3, Role: 'Human Resource Manager', Team: 1 },
{ Id: 4, Role: 'Design Manager', Team: 1 },
{ Id: 5, Role: 'Operation Manager', Team: 1 },
{ Id: 6, Role: 'Marketing Manager', Team: 1 },
];
//Sets the default properties for all the Nodes
public getNodeDefaults(node: NodeModel): NodeModel {
node.width = 150;
node.height = 50;
node.annotations = [{ content: (node.data as { Role: 'string' }).Role }];
return node;
}
//Sets the default properties for all the connectors
public getConnectorDefaults(connector: ConnectorModel): ConnectorModel {
connector.type = 'Orthogonal';
(connector.targetDecorator as DecoratorModel).shape = 'None';
return connector;
}
ngOnInit(): void {
this.snapSettings = { constraints: 0 };
this.items = new DataManager(this.data as JSON[], new Query().take(7));
//Uses layout to auto-arrange nodes on the Diagram page
this.layout = {
//Sets layout type
type: 'OrganizationalChart',
// define the getLayoutInfo
getLayoutInfo: (node: Node, options: TreeInfo) => {
if (!options.hasSubTree) {
options.type = 'Center';
options.orientation = 'Horizontal';
}
},
};
//Configures data source for Diagram
this.dataSourceSettings = {
id: 'Id',
parentId: 'Team',
dataSource: this.items
};
};
public onAlignmentChange(event: Event) {
const args = event.target as HTMLInputElement;
this.diagram.layout.getLayoutInfo = (node: Node, options: TreeInfo) => {
if (!options.hasSubTree) {
options.type = (args.value as SubTreeAlignments);
options.orientation = 'Horizontal';
}
};
};
}
import { bootstrapApplication } from '@angular/platform-browser';
import { AppComponent } from './app.component';
import 'zone.js';
bootstrapApplication(AppComponent).catch((err) => console.error(err));
Vertical Subtree Orientation Example
This example shows how to implement vertical arrangement for leaf-level organizational trees:
import { Component, ViewEncapsulation, ViewChild } from '@angular/core';
import { DiagramComponent, Diagram, NodeModel, ConnectorModel, SnapSettingsModel, LayoutModel, DataSourceModel, DiagramModule,
HierarchicalTreeService, DataBindingService, TreeInfo, DataBinding, HierarchicalTree, SubTreeAlignments, DecoratorModel } from '@syncfusion/ej2-angular-diagrams';
import { DataManager, Query } from '@syncfusion/ej2-data';
Diagram.Inject(DataBinding, HierarchicalTree);
@Component({
imports: [ DiagramModule ],
providers: [HierarchicalTreeService, DataBindingService],
standalone: true,
selector: "app-container",
template: `
<label for="alignment">Alignment: </label>
<select id="alignment" #alignment (change)="onAlignmentChange($event)">
<option value="Left">Left</option>
<option value="Right">Right</option>
<option value="Alternate">Alternate</option>
</select>
<ejs-diagram #diagram id="diagram" width="100%" height="530px" [getNodeDefaults]="getNodeDefaults" [getConnectorDefaults]="getConnectorDefaults"
[snapSettings]="snapSettings" [layout]="layout" [dataSourceSettings]="dataSourceSettings"> </ejs-diagram>`,
encapsulation: ViewEncapsulation.None
})
export class AppComponent {
@ViewChild("diagram")
public diagram!: DiagramComponent;
public snapSettings?: SnapSettingsModel;
public items?: DataManager;
public layout?: LayoutModel;
public dataSourceSettings?: DataSourceModel;
//Initializes data source
public data: object[] = [
{ Id: 1, Role: 'General Manager' },
{ Id: 2, Role: 'Assistant Manager', Team: 1 },
{ Id: 3, Role: 'Human Resource Manager', Team: 1 },
{ Id: 4, Role: 'Design Manager', Team: 1 },
{ Id: 5, Role: 'Operation Manager', Team: 1 },
{ Id: 6, Role: 'Marketing Manager', Team: 1 },
];
//Sets the default properties for all the Nodes
public getNodeDefaults(node: NodeModel): NodeModel {
node.width = 150;
node.height = 50;
node.annotations = [{ content: (node.data as { Role: 'string' }).Role }];
return node;
}
//Sets the default properties for all the connectors
public getConnectorDefaults(connector: ConnectorModel): ConnectorModel {
connector.type = 'Orthogonal';
(connector.targetDecorator as DecoratorModel).shape = 'None';
return connector;
}
ngOnInit(): void {
this.snapSettings = { constraints: 0 };
this.items = new DataManager(this.data as JSON[], new Query().take(7));
//Uses layout to auto-arrange nodes on the Diagram page
this.layout = {
//Sets layout type
type: 'OrganizationalChart',
// define the getLayoutInfo
getLayoutInfo: (node: Node, options: TreeInfo) => {
if (!options.hasSubTree) {
options.type = 'Left';
options.orientation = 'Vertical';
}
},
};
//Configures data source for Diagram
this.dataSourceSettings = {
id: 'Id',
parentId: 'Team',
dataSource: this.items
};
};
public onAlignmentChange(event: Event) {
const args = event.target as HTMLInputElement;
this.diagram.layout.getLayoutInfo = (node: Node, options: TreeInfo) => {
if (!options.hasSubTree) {
options.type = (args.value as SubTreeAlignments);
options.orientation = 'Vertical';
}
};
};
}
import { bootstrapApplication } from '@angular/platform-browser';
import { AppComponent } from './app.component';
import 'zone.js';
bootstrapApplication(AppComponent).catch((err) => console.error(err));
Assistant Nodes
Assistant nodes represent positions with specialized relationships to their parent, such as executive assistants or advisor’s. These nodes are positioned in a dedicated area separate from regular child nodes. To designate a node as an assistant, add it to the assistants
collection within the getLayoutInfo
options parameter.
import { Component, ViewEncapsulation, ViewChild } from '@angular/core';
import { DiagramComponent, Diagram, NodeModel, ConnectorModel, SnapSettingsModel, LayoutModel, DataSourceModel, DiagramModule,
HierarchicalTreeService, DataBindingService, TreeInfo, DataBinding, HierarchicalTree, DecoratorModel } from '@syncfusion/ej2-angular-diagrams';
import { DataManager, Query } from '@syncfusion/ej2-data';
Diagram.Inject(DataBinding, HierarchicalTree);
@Component({
imports: [ DiagramModule ],
providers: [HierarchicalTreeService, DataBindingService],
standalone: true,
selector: "app-container",
template: `
<ejs-diagram #diagram id="diagram" width="100%" height="530px" [getNodeDefaults]="getNodeDefaults" [getConnectorDefaults]="getConnectorDefaults"
[snapSettings]="snapSettings" [layout]="layout" [dataSourceSettings]="dataSourceSettings"> </ejs-diagram>`,
encapsulation: ViewEncapsulation.None
})
export class AppComponent {
@ViewChild("diagram")
public diagram!: DiagramComponent;
public snapSettings?: SnapSettingsModel;
public items?: DataManager;
public layout?: LayoutModel;
public dataSourceSettings?: DataSourceModel;
//Initializes data source
public data: object[] = [
{ Id: 1, Role: 'General Manager' },
{ Id: 2, Role: 'Assistant Manager', Team: 1 },
{ Id: 3, Role: 'Human Resource Manager', Team: 1 },
{ Id: 4, Role: 'Design Manager', Team: 1 },
{ Id: 5, Role: 'Operation Manager', Team: 1 },
{ Id: 6, Role: 'Marketing Manager', Team: 1 },
];
//Sets the default properties for all the Nodes
public getNodeDefaults(node: NodeModel): NodeModel {
node.width = 150;
node.height = 50;
node.annotations = [{ content: (node.data as { Role: 'string' }).Role }];
return node;
}
//Sets the default properties for all the connectors
public getConnectorDefaults(connector: ConnectorModel): ConnectorModel {
connector.type = 'Orthogonal';
(connector.targetDecorator as DecoratorModel).shape = 'None';
return connector;
}
ngOnInit(): void {
this.snapSettings = { constraints: 0 };
this.items = new DataManager(this.data as JSON[], new Query().take(7));
//Uses layout to auto-arrange nodes on the Diagram page
this.layout = {
//Sets layout type
type: 'OrganizationalChart',
// define the getLayoutInfo
getLayoutInfo: (node: Node | any, options: TreeInfo) => {
if (node.data['Role'] === 'General Manager') {
(options.assistants as string[]).push((options.children as string[])[0]);
(options.children as string[]).splice(0, 1);
}
if (!options.hasSubTree) {
options.type = 'Center';
options.orientation = 'Horizontal';
}
},
};
//Configures data source for Diagram
this.dataSourceSettings = {
id: 'Id',
parentId: 'Team',
dataSource: this.items
};
};
}
import { bootstrapApplication } from '@angular/platform-browser';
import { AppComponent } from './app.component';
import 'zone.js';
bootstrapApplication(AppComponent).catch((err) => console.error(err));
Important: Assistant nodes cannot have child nodes and serve as terminal positions in the organizational hierarchy.
Best Practices
- Use the DataSource approach for dynamic organizational structures that may change frequently
- Implement the manual approach when requiring custom node designs or static hierarchies
- Consider using assistant nodes for specialized roles like executive assistants or advisory positions
- Apply appropriate orientation and alignment settings based on the size and complexity of the organization
- Test layout performance with large datasets and consider implementing virtualization for extensive organizational charts