Complex hierarchical tree layout in Angular Diagram control
23 Aug 202523 minutes to read
Complex hierarchical tree layout arranges nodes in a tree-like structure where child nodes can have multiple parent nodes, creating interconnected relationships beyond traditional single-parent hierarchies. This layout type is ideal for organizational charts with dotted-line relationships, project dependencies, or any structure where entities report to multiple authorities. This layout extends the standard hierarchical tree layout to support these complex relationships.
To create a complex hierarchical tree, set the type
property of layout to ComplexHierarchicalTree
.
Complex hierarchical tree layout with nodes and connectors
This example demonstrates how to create a complex hierarchical tree layout by manually defining nodes and connectors. The layout automatically positions nodes based on their hierarchical relationships while handling multiple parent-child connections.
import { Component, ViewEncapsulation, ViewChild } from '@angular/core';
import { DiagramModule, DiagramComponent, NodeModel, ConnectorModel, Diagram, DataBinding,
ComplexHierarchicalTree, LayoutModel } from '@syncfusion/ej2-angular-diagrams';
Diagram.Inject(DataBinding, ComplexHierarchicalTree);
@Component({
imports: [ DiagramModule ],
providers: [],
standalone: true,
selector: 'app-container',
template: `<ejs-diagram #diagram id="diagram" width="100%" height="1000px" [nodes]='nodes' [connectors]='connectors'
[getConnectorDefaults]='connectorDefaults' [getNodeDefaults]='nodeDefaults' [layout]='layout'></ejs-diagram>`,
encapsulation: ViewEncapsulation.None
})
export class AppComponent {
@ViewChild('diagram')
public diagram?: DiagramComponent;
//Initialize nodes for diagram
public nodes: NodeModel[] = [
{ id: 'node1' },
{ id: 'node2' },
{ id: 'node3' },
{ id: 'node4' },
{ id: 'node5' },
{ id: 'node6' },
{ id: 'node7' },
];
//Initialize connectors for diagram
public connectors: ConnectorModel[] = [
{ id: 'node1-node4', sourceID: 'node1', targetID: 'node4' },
{ id: 'node2-node4', sourceID: 'node2', targetID: 'node4' },
{ id: 'node3-node4', sourceID: 'node3', targetID: 'node4' },
{ id: 'node4-node5', sourceID: 'node4', targetID: 'node5' },
{ id: 'node4-node6', sourceID: 'node4', targetID: 'node6' },
{ id: 'node5-node6', sourceID: 'node6', targetID: 'node7' },
];
//Uses layout to auto-arrange nodes on the Diagram page
public layout: LayoutModel = {
//Sets layout type
type: 'ComplexHierarchicalTree',
};
//Sets the default properties for all the Nodes
public nodeDefaults(node: NodeModel): NodeModel {
node.width = 70; node.height = 70;
node.annotations = [{ content: node.id }];
return node;
};
//Sets the default properties for all the connectors
public connectorDefaults(connector: ConnectorModel): ConnectorModel {
connector.type = 'Orthogonal';
return connector;
};
}
import { bootstrapApplication } from '@angular/platform-browser';
import { AppComponent } from './app.component';
import 'zone.js';
bootstrapApplication(AppComponent).catch((err) => console.error(err));
Complex hierarchical tree layout with DataSource
When working with large datasets, binding the layout to a data source provides better maintainability and dynamic content management. The following example shows how to create a complex hierarchical tree using a data source configuration.
import { Component, ViewEncapsulation, ViewChild } from '@angular/core';
import { DiagramModule, HierarchicalTreeService, DataBindingService, NodeModel, ConnectorModel, Diagram,
DataBinding, ComplexHierarchicalTree, LayoutModel, DiagramComponent } from '@syncfusion/ej2-angular-diagrams'
import { DataManager } from '@syncfusion/ej2-data';
Diagram.Inject(DataBinding, ComplexHierarchicalTree);
@Component({
imports: [DiagramModule],
providers: [HierarchicalTreeService, DataBindingService],
standalone: true,
selector: 'app-container',
template: `<ejs-diagram #diagram id="diagram" width="100%" height="1000px" [getNodeDefaults]='nodeDefaults'
[getConnectorDefaults]='connectorDefaults' [layout]='layout' [dataSourceSettings]='data'></ejs-diagram>`,
encapsulation: ViewEncapsulation.None
})
export class AppComponent {
@ViewChild('diagram')
public diagram?: DiagramComponent;
//Initializes data source
public data: Object = {
id: 'Name', parentId: 'ReportingPerson',
dataSource: new DataManager([
{ "Name": "node11" },
{ "Name": "node12", "ReportingPerson": ["node114"] },
{ "Name": "node13", "ReportingPerson": ["node12"] },
{ "Name": "node14", "ReportingPerson": ["node12"] },
{ "Name": "node15", "ReportingPerson": ["node12"] },
{ "Name": "node16", "ReportingPerson": [] },
{ "Name": "node17", "ReportingPerson": ["node13", "node14", "node15"] },
{ "Name": "node18", "ReportingPerson": [] },
{ "Name": "node19", "ReportingPerson": ["node16", "node17", "node18"] },
{ "Name": "node110", "ReportingPerson": ["node16", "node17", "node18"] },
{ "Name": "node111", "ReportingPerson": ["node16", "node17", "node18", "node116"] },
{ "Name": "node21" },
{ "Name": "node22", "ReportingPerson": ["node114"] },
{ "Name": "node23", "ReportingPerson": ["node22"] },
{ "Name": "node24", "ReportingPerson": ["node22"] },
{ "Name": "node25", "ReportingPerson": ["node22"] },
{ "Name": "node26", "ReportingPerson": [] },
{ "Name": "node27", "ReportingPerson": ["node23", "node24", "node25"] },
{ "Name": "node28", "ReportingPerson": [] },
{ "Name": "node29", "ReportingPerson": ["node26", "node27", "node28", "node116"] },
{ "Name": "node210", "ReportingPerson": ["node26", "node27", "node28"] },
{ "Name": "node211", "ReportingPerson": ["node26", "node27", "node28"] },
{ "Name": "node31" },
{ "Name": "node114", "ReportingPerson": ["node11", "node21", "node31"] },
{ "Name": "node116", "ReportingPerson": ["node12", "node22"], }
],)
};
//Sets the default properties for all the Nodes
public nodeDefaults(node: NodeModel): NodeModel {
node.width = 70; node.height = 70;
return node;
};
//Sets the default properties for all the connectors
public connectorDefaults(connector: ConnectorModel): ConnectorModel {
connector.type = 'Orthogonal';
return connector;
};
//Uses layout to auto-arrange nodes on the Diagram page
public layout: LayoutModel = {
//Sets layout type
type: 'ComplexHierarchicalTree',
};
}
import { bootstrapApplication } from '@angular/platform-browser';
import { AppComponent } from './app.component';
import 'zone.js';
bootstrapApplication(AppComponent).catch((err) => console.error(err));
Note: In Diagram layouts, all root nodes will always render at the same level. This default behavior cannot be changed to render different trees at distinct levels.
Line Distribution
Line distribution prevents connector overlap by controlling how multiple connectors from a single parent node are positioned. Without line distribution, connectors may overlap and create visual confusion in complex layouts.
The connectionPointOrigin
property controls this behavior:
- SamePoint (default): All connectors from a parent originate from the same point
- DifferentPoint: Each connector originates from a different point, distributing connections around the node
import { Component, ViewEncapsulation, ViewChild } from '@angular/core';
import { NodeModel, ConnectorModel, Diagram, DataBinding, ComplexHierarchicalTree, LayoutModel, LineDistribution,
ConnectionPointOrigin, DiagramModule, HierarchicalTreeService, DataBindingService, DiagramComponent } from '@syncfusion/ej2-angular-diagrams';
import { DataManager, Query } from '@syncfusion/ej2-data';
Diagram.Inject(DataBinding, ComplexHierarchicalTree, LineDistribution);
@Component({
imports: [DiagramModule],
providers: [HierarchicalTreeService, DataBindingService],
standalone: true,
selector: 'app-container',
template: `<ejs-diagram #diagram id="diagram" width="100%" height="590px"
[getNodeDefaults]='nodeDefaults' [getConnectorDefaults]='connectorDefaults'
[layout]='layout' [dataSourceSettings]='source'></ejs-diagram>`,
encapsulation: ViewEncapsulation.None
})
export class AppComponent {
@ViewChild('diagram')
public diagram?: DiagramComponent;
//Initializes data source
public data: Object[] = [
{ "Name": "node11" },
{ "Name": "node12", "ReportingPerson": ["node114"] },
{ "Name": "node13", "ReportingPerson": ["node12"] },
{ "Name": "node14", "ReportingPerson": ["node12"] },
{ "Name": "node15", "ReportingPerson": ["node12"] },
{ "Name": "node116", "ReportingPerson": ["node22", "node12"] },
{ "Name": "node16", "ReportingPerson": [] },
{ "Name": "node18", "ReportingPerson": [] },
{ "Name": "node21" },
{ "Name": "node22", "ReportingPerson": ["node114"] },
{ "Name": "node23", "ReportingPerson": ["node22"] },
{ "Name": "node24", "ReportingPerson": ["node22"] },
{ "Name": "node25", "ReportingPerson": ["node22"] },
{ "Name": "node26", "ReportingPerson": [] },
{ "Name": "node28", "ReportingPerson": [] },
{ "Name": "node31" },
{ "Name": "node114", "ReportingPerson": ["node11", "node21", "node31"] }
];
//Configures data source for diagram
public source: Object = {
id: 'Name', parentId: 'ReportingPerson',
dataSource: new DataManager(this.data as JSON[], new Query().take(7))
};
//Sets the default properties for all the Nodes
public nodeDefaults(node: NodeModel): NodeModel {
node.width = 40; node.height = 40;
return node;
};
//Sets the default properties for all the connectors
public connectorDefaults(connector: ConnectorModel): ConnectorModel {
connector.type = 'Orthogonal';
connector.cornerRadius = 7;
return connector;
};
//Uses layout to auto-arrange nodes on the Diagram page
public layout: LayoutModel = {
//Sets layout type
type: 'ComplexHierarchicalTree',
connectionPointOrigin: ConnectionPointOrigin.DifferentPoint,
};
}
import { bootstrapApplication } from '@angular/platform-browser';
import { AppComponent } from './app.component';
import 'zone.js';
bootstrapApplication(AppComponent).catch((err) => console.error(err));
Note: Line distribution requires the LineDistribution module to be injected in the diagram.
Linear Arrangement
Linear arrangement positions child nodes in a straight line with their parent node centered relative to the children. This creates a more organized appearance when dealing with nodes that have multiple children.
When line distribution is enabled, linear arrangement activates automatically. The arrangement
property provides control over this feature:
- Nonlinear (default): Child nodes are arranged based on available space
- Linear: Child nodes are arranged in a straight line with the parent centered
import { Component, ViewEncapsulation, ViewChild } from '@angular/core';
import { DiagramComponent, DiagramModule, HierarchicalTreeService, DataBindingService, NodeModel, ConnectorModel,
Diagram, DataBinding, ComplexHierarchicalTree, LayoutModel, LineDistribution, ChildArrangement
} from '@syncfusion/ej2-angular-diagrams';
import { DataManager, Query } from '@syncfusion/ej2-data';
Diagram.Inject(DataBinding, ComplexHierarchicalTree, LineDistribution);
@Component({
imports: [DiagramModule],
providers: [HierarchicalTreeService, DataBindingService],
standalone: true,
selector: 'app-container',
template: `<ejs-diagram #diagram id="diagram" width="100%" height="590px" [getNodeDefaults]='nodeDefaults'
[getConnectorDefaults]='connectorDefaults' [layout]='layout' [dataSourceSettings]='source' ></ejs-diagram>`,
encapsulation: ViewEncapsulation.None
})
export class AppComponent {
@ViewChild('diagram')
public diagram?: DiagramComponent;
//Initializes data source
public data: Object[] = [
{ "Name": "node11" },
{ "Name": "node12", "ReportingPerson": ["node114"] },
{ "Name": "node13", "ReportingPerson": ["node12"] },
{ "Name": "node14", "ReportingPerson": ["node12"] },
{ "Name": "node15", "ReportingPerson": ["node12"] },
{ "Name": "node116", "ReportingPerson": ["node22", "node12"] },
{ "Name": "node16", "ReportingPerson": [] },
{ "Name": "node18", "ReportingPerson": [] },
{ "Name": "node21" },
{ "Name": "node22", "ReportingPerson": ["node114"] },
{ "Name": "node23", "ReportingPerson": ["node22"] },
{ "Name": "node24", "ReportingPerson": ["node22"] },
{ "Name": "node25", "ReportingPerson": ["node22"] },
{ "Name": "node26", "ReportingPerson": [] },
{ "Name": "node28", "ReportingPerson": [] },
{ "Name": "node31" },
{ "Name": "node114", "ReportingPerson": ["node11", "node21", "node31"] }
];
//Configures data source for diagram
public source: Object = {
id: 'Name', parentId: 'ReportingPerson',
dataSource: new DataManager(this.data as JSON[], new Query().take(7))
};
//Sets the default properties for all the Nodes
public nodeDefaults(node: NodeModel): NodeModel {
node.width = 40; node.height = 40;
return node;
};
//Sets the default properties for all the connectors
public connectorDefaults(connector: ConnectorModel): ConnectorModel {
connector.type = 'Orthogonal';
connector.cornerRadius = 7;
return connector;
};
//Uses layout to auto-arrange nodes on the Diagram page
public layout: LayoutModel = {
//Sets layout type
type: 'ComplexHierarchicalTree',
arrangement: ChildArrangement.Linear
};
}
import { bootstrapApplication } from '@angular/platform-browser';
import { AppComponent } from './app.component';
import 'zone.js';
bootstrapApplication(AppComponent).catch((err) => console.error(err));
Note: Linear arrangement requires the LineDistribution module and is applicable only for complex hierarchical tree layouts.
Enable routing for layout
In complex diagrams with intricate parent-child relationships, connectors may pass through or overlap with nodes, making the diagram difficult to read. Routing functionality automatically calculates connector paths that avoid intersecting with nodes and other obstacles.
Set the enableRouting
property to true
to activate intelligent connector routing.
import { Component, ViewEncapsulation, ViewChild } from '@angular/core';
import { DiagramModule, HierarchicalTreeService, DataBindingService, DiagramComponent, NodeModel, ConnectorModel,
Diagram, DataBinding, ComplexHierarchicalTree, LayoutModel, LineDistribution } from '@syncfusion/ej2-angular-diagrams';
import { DataManager, Query } from '@syncfusion/ej2-data';
Diagram.Inject(DataBinding, ComplexHierarchicalTree, LineDistribution );
@Component({
imports: [ DiagramModule ],
providers: [HierarchicalTreeService, DataBindingService],
standalone: true,
selector: 'app-container',
template: `<ejs-diagram #diagram id="diagram" width="100%" height="590px" [getNodeDefaults]='nodeDefaults'
[getConnectorDefaults]='connectorDefaults' [layout]='layout' [dataSourceSettings]='source' ></ejs-diagram>`,
encapsulation: ViewEncapsulation.None
})
export class AppComponent {
@ViewChild('diagram')
public diagram?: DiagramComponent;
//Initializes data source
public data: Object[] =[
{ Name: 'node11' },
{ Name: 'node12', ReportingPerson: ['node114'] },
{ Name: 'node13', ReportingPerson: ['node12'] },
{ Name: 'node14', ReportingPerson: ['node12'] },
{ Name: 'node15', ReportingPerson: ['node12'] },
{ Name: 'node16', ReportingPerson: ['node12'] },
{ Name: 'node116', ReportingPerson: ['node22', 'node12', 'node114'] },
{ Name: 'node21' },
{ Name: 'node22', ReportingPerson: ['node114'] },
{ Name: 'node222', ReportingPerson: ['node114'] },
{ Name: 'node2222', ReportingPerson: ['node114'] },
{ Name: 'node23', ReportingPerson: ['node22'] },
{ Name: 'node31' },
{ Name: 'node114', ReportingPerson: ['node11', 'node21', 'node31'] },
];
//Configures data source for diagram
public source: Object = {
id: 'Name', parentId: 'ReportingPerson',
dataSource: new DataManager(this.data as JSON[], new Query().take(7))
};
//Sets the default properties for all the Nodes
public nodeDefaults(node: NodeModel): NodeModel {
node.width = 40; node.height = 40;
return node;
};
//Sets the default properties for all the connectors
public connectorDefaults(connector: ConnectorModel): ConnectorModel {
connector.type = 'Orthogonal';
connector.cornerRadius = 7;
return connector;
};
//Uses layout to auto-arrange nodes on the Diagram page
public layout: LayoutModel = {
//Sets layout type
type: 'ComplexHierarchicalTree',
enableRouting: true
};
}
import { bootstrapApplication } from '@angular/platform-browser';
import { AppComponent } from './app.component';
import 'zone.js';
bootstrapApplication(AppComponent).catch((err) => console.error(err));
Best Practices
- Use data source binding for dynamic content and better maintainability
- Enable line distribution when dealing with nodes that have multiple connections
- Consider enabling routing for complex layouts to improve visual clarity
- Test layout performance with large datasets and optimize as needed
- Ensure proper module injection for advanced features like line distribution