Layout events in Angular Diagram control

23 Aug 202513 minutes to read

Layout events in the Angular Diagram component provide developers with hooks to respond to various stages of automatic layout processing. These events are particularly useful when working with hierarchical data structures and need to customize behavior during layout rendering, data loading, or node expansion/collapse operations.

The diagram component supports several layout-specific events that fire during different phases of the layout life cycle, enabling fine-grained control over layout behavior and user interactions.

DataLoaded event

The dataLoaded event triggers after the diagram successfully populates from an external data source. This event provides access to the loaded data and diagram instance, making it ideal for performing post-load customizations such as applying custom styling, setting initial node states, or configuring layout-specific properties.

The event fires once the data binding process completes but before the initial layout calculation begins, providing an opportunity to modify nodes or connectors before they are positioned.

import { Component, ViewChild } from '@angular/core';
import { DiagramComponent, IDataLoadedEventArgs } from '@syncfusion/ej2-angular-diagrams';

<ejs-diagram #diagram id="diagram" width="100%" height="600px" [nodes]="nodes" [connectors]="connectors"
  [layout]="layout" (dataLoaded)="dataLoaded($event)" > </ejs-diagram>

export class AppComponent {
  @ViewChild("diagram")
  public diagram!: DiagramComponent;

  public dataLoaded(args: IDataLoadedEventArgs): void {
      // Access the diagram instance and perform post-load customizations
      console.log('Data loaded successfully:', args);
      
      // Example: Apply custom styling to root nodes
      // Custom logic here
  }
}

ExpandStateChange event

The expandStateChange event fires when a user clicks the expand or collapse icon of a node in a hierarchical layout. This event occurs before the layout update begins, allowing developers to prevent the state change, modify the expansion behavior, or trigger custom actions based on the node’s new state.

The event provides information about the affected node, its current state, and whether the operation can be canceled. This makes it valuable for implementing conditional expansion, loading child data on-demand, or applying custom animations.

import { Component, ViewEncapsulation, ViewChild } from '@angular/core';
import { DiagramComponent, Diagram, NodeModel, ConnectorModel, LayoutModel, HierarchicalTree, DiagramModule,
  HierarchicalTreeService, IExpandStateChangeEventArgs } from '@syncfusion/ej2-angular-diagrams';

Diagram.Inject(HierarchicalTree);

@Component({
  imports: [DiagramModule],

  providers: [HierarchicalTreeService],
  standalone: true,
  selector: "app-container",
  template: `
  <ejs-diagram #diagram id="diagram" width="100%" height="550px" [nodes]="nodes" [connectors]="connectors"
  [layout]="layout" (expandStateChange)="expandStateChange($event)" > </ejs-diagram>`,
  encapsulation: ViewEncapsulation.None
})

export class AppComponent {
  @ViewChild("diagram")
  public diagram!: DiagramComponent;
  public layout: LayoutModel = {};

  //Initialize nodes 
  public nodes: NodeModel[] = [
    {
      id: 'Start',
      width: 140,
      height: 50,
      offsetX: 300,
      offsetY: 50,
      annotations: [{ content: 'Node1' }],
      style: { fill: '#6BA5D7', strokeColor: 'white' },
      expandIcon: {
        shape: 'ArrowDown',
        width: 20,
        height: 15,
      },
      collapseIcon: {
        shape: 'ArrowUp',
        width: 20,
        height: 15,
      },
    },
    {
      id: 'Init',
      width: 140,
      height: 50,
      offsetX: 300,
      offsetY: 140,
      style: { fill: '#6BA5D7', strokeColor: 'white' },
      annotations: [{ content: 'Node2' }],
    },
  ];

  //Initialize connectors
  public connectors: ConnectorModel[] = [{
    // Unique name for the connector
    id: 'connector1',
    sourceID: 'Start',
    targetID: 'Init',
    type: 'Orthogonal',
  }];

  // Handle expand/collapse state changes
  public expandStateChange(args: IExpandStateChangeEventArgs) {
    //We can get the expanded or collapsed state in args
    console.log('Expanded ' + args.state);
  }

  ngOnInit(): void {
    //Uses layout to auto-arrange nodes on the Diagram page
    this.layout = {
      //Sets layout type
      type: 'HierarchicalTree',
    }
  }

};
import { bootstrapApplication } from '@angular/platform-browser';
import { AppComponent } from './app.component';
import 'zone.js';
bootstrapApplication(AppComponent).catch((err) => console.error(err));

Animation complete event

The animationComplete event triggers after the diagram finishes animating layout changes, particularly during expand and collapse operations. This event is essential for detecting when visual transitions have completed, enabling developers to perform follow-up actions such as scrolling to specific nodes, updating UI indicators, or triggering additional layout adjustments.

The event fires at the end of the animation cycle, ensuring that all visual updates are complete before any subsequent operations begin.

import { Component, ViewEncapsulation, ViewChild } from '@angular/core';
import { DiagramComponent, Diagram, NodeModel, ConnectorModel, LayoutModel, HierarchicalTree, DiagramModule,
  HierarchicalTreeService, LayoutAnimation, LayoutAnimationService, ShapeStyleModel } from '@syncfusion/ej2-angular-diagrams';

Diagram.Inject(HierarchicalTree, LayoutAnimation);

@Component({
  imports: [DiagramModule],

  providers: [HierarchicalTreeService, LayoutAnimationService],
  standalone: true,
  selector: "app-container",
  template: `
  <ejs-diagram #diagram id="diagram" width="100%" height="600px" [nodes]="nodes" [connectors]="connectors"
  [layout]="layout" (animationComplete)="animationComplete()" > </ejs-diagram>`,
  encapsulation: ViewEncapsulation.None
})

export class AppComponent {
  @ViewChild("diagram")
  public diagram!: DiagramComponent;
  public layout: LayoutModel = {};

  //Initialize nodes
  public nodes: NodeModel[] = [
    {
      id: 'Start',
      width: 140,
      height: 50,
      offsetX: 300,
      offsetY: 50,
      annotations: [{ content: 'Node1' }],
      style: { fill: '#6BA5D7', strokeColor: 'white' },
      expandIcon: {
        shape: 'ArrowDown',
        width: 20,
        height: 15,
      },
      collapseIcon: {
        shape: 'ArrowUp',
        width: 20,
        height: 15,
      },
    },
    {
      id: 'Init',
      width: 140,
      height: 50,
      offsetX: 300,
      offsetY: 140,
      style: { fill: '#6BA5D7', strokeColor: 'white' },
      annotations: [{ content: 'Node2' }],
    },
    {
      id: 'Init2',
      width: 140,
      height: 50,
      offsetX: 100,
      offsetY: 140,
      style: { fill: '#6BA5D7', strokeColor: 'white' },
      annotations: [{ content: 'Node3' }],
    },
    {
      id: 'Init3',
      width: 140,
      height: 50,
      offsetX: 150,
      offsetY: 140,
      style: { fill: '#6BA5D7', strokeColor: 'white' },
      annotations: [{ content: 'Node4' }],
    },
  ];

  //Initialize connectors
  public connectors: ConnectorModel[] = [
    {
      id: 'connector1',
      sourceID: 'Start',
      targetID: 'Init',
      type: 'Orthogonal',
    },
    {
      id: 'connector2',
      sourceID: 'Start',
      targetID: 'Init2',
      type: 'Orthogonal',
    },
    {
      id: 'connector3',
      sourceID: 'Init2',
      targetID: 'Init3',
      type: 'Orthogonal',
    },
  ];

  // Handle animation state changes
  public animationComplete() {

    console.log('Animation complete');
    (this.diagram.nodes[0].style as ShapeStyleModel).fill =
      (this.diagram.nodes[0].style as ShapeStyleModel).fill === '#6BA5D7' ? 'red' : '#6BA5D7';
    this.diagram.dataBind();

    //customize
  }

  ngOnInit(): void {
    //Uses layout to auto-arrange nodes on the Diagram page
    this.layout = {
      //Sets layout type
      type: 'HierarchicalTree',
    }
  }

};
import { bootstrapApplication } from '@angular/platform-browser';
import { AppComponent } from './app.component';
import 'zone.js';
bootstrapApplication(AppComponent).catch((err) => console.error(err));

Layout updated event

The layoutUpdated event fires at both the beginning and completion of the layout rendering process. This event enables tracking of layout calculation progress and provides timing information for performance monitoring or progress indication purposes.

The event includes a state parameter that indicates whether the layout process is starting or finishing, allowing developers to implement loading indicators, measure layout performance, or coordinate with other application components that depend on layout completion.

import { Component, ViewChild } from '@angular/core';
import { DiagramComponent, ILayoutUpdatedEventArgs } from '@syncfusion/ej2-angular-diagrams';

<ejs-diagram #diagram id="diagram" width="100%" height="550px" [nodes]="nodes" [connectors]="connectors"
  [layout]="layout" (layoutUpdated)="layoutUpdated($event)" > </ejs-diagram>

export class AppComponent {
  @ViewChild("diagram")
  public diagram!: DiagramComponent;
  
  // Handle layout updated event for progress tracking
  public layoutUpdated(args: ILayoutUpdatedEventArgs): void {
      if (args.state === 'Started') {
          console.log('Layout calculation started');
          // Show loading indicator
      } else if (args.state === 'Completed') {
          console.log('Layout rendering completed');
          // Hide loading indicator, perform post-layout actions
      }
  }
}

These layout events work together to provide comprehensive control over the automatic layout life cycle, from initial data loading through final rendering completion. They enable developers to create responsive, interactive diagram experiences with proper feedback and customization capabilities.