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:

  1. DataSource approach - Recommended for dynamic data scenarios
  2. 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));

Organizational chart

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 Horizontal Left
  Right Positions child nodes horizontally to the right of the parent Horizontal Right
  Center Arranges children in standard tree layout format Horizontal Center
  Balanced Distributes leaf-level nodes across multiple rows for better space utilization Horizontal Balanced
Vertical Left Arranges children vertically on the left side of the parent Vertical Left
  Right Arranges children vertically on the right side of the parent Vertical Right
  Alternate Distributes children on both left and right sides of the parent Vertical Alternate

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));

Assistant

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