Save and Load Diagrams in Angular Diagram Component

25 Aug 202518 minutes to read

Serialization is the process of converting the diagram’s current state into a storage format that can be saved and later restored. This feature ensures that all diagram elements, including nodes, connectors, and their configurations, persist across application sessions.

The serialization process converts the diagram into a JSON string format, which can be stored in databases, files, or other storage systems. When needed, this serialized data can be deserialized to recreate the diagram exactly as it was previously configured.

Use serialization when you need to:

  • Save user-created diagrams for future editing
  • Implement undo/redo functionality
  • Create diagram templates
  • Transfer diagrams between different sessions or users

To save and load the diagram in Angular, refer to the below video link.

Saving Diagrams

Basic Save Operation

The saveDiagram method serializes the entire diagram configuration into a JSON string. This method captures all diagram elements, their properties, and the current state.

//returns serialized string of the Diagram
saveData = this.diagram.saveDiagram();

The serialized JSON string can be stored in various storage systems. The following example demonstrates local storage implementation:

//Saves the string in to local storage
localStorage.setItem('fileName', saveData);

// Retrieve the saved string from local storage
saveData = localStorage.getItem('fileName');

Alternative Save Formats

The diagram can also be saved as raster or vector image files. For more information about saving the diagram as images, refer to the Print and Export sections.

Loading Diagrams

Basic Load Operation

The loadDiagram method recreates the diagram from serialized JSON data. This method accepts the previously saved JSON string as a parameter.

export class AppComponent {
  @ViewChild('diagram', { static: true }) diagram: DiagramComponent;
  
  loadDiagram(): void {
    const savedData: string = localStorage.getItem('diagramData');
    
    if (savedData) {
      /*
       * Loads the diagram from saved JSON data.
       * parameter 1 - The string representing the diagram model JSON to be loaded.
       * parameter 2 - Whether it is ej1 data or not (optional)
       */
      this.diagram.loadDiagram(savedData);
      console.log('Diagram loaded successfully');
    } else {
      console.warn('No saved diagram data found');
    }
  }
}

NOTE

Before loading a new diagram, the existing diagram content is automatically cleared.

Handling Load Completion

The loaded event triggers when all diagram elements finish loading through the loadDiagram method. Use this event to perform post-load customizations or validations.

<ejs-diagram #diagram id="diagram" width="100%" height="700px" (loaded)="loaded()" >
</ejs-diagram>
export class AppComponent {
  public loaded(args:ILoadedEventArgs): void {
      //You can use this event to customize diagram elements during the loading process
  }
}

The loaded event provides the following arguments:

name

  • Type: String
  • Description: Returns the event name

diagram

  • Type: Diagram
  • Description: Returns the complete diagram model with all properties

Optimizing Serialized Data

Preventing Default Values

The preventDefaults property within serializationSettings reduces the size of serialized data by excluding default properties. This optimization improves performance when handling large diagrams or frequent save operations.

When enabled, only explicitly set properties are included in the JSON output, significantly reducing file size and improving load times.

<ejs-diagram #diagram id="diagram" width="100%" height="700px" (serializationSettings)="serializationSettings" >
</ejs-diagram>
export class AppComponent {
  public serializationSettings: SerializationSettingsModel = {};
  
  ngOnInit() {
    this.serializationSettings = { preventDefaults: true };
  }
}

File-Based Save and Load Operations

Using Uploader Component

JSON files can be uploaded and processed using the uploader component. Configure the uploader with appropriate server endpoints to handle file operations, then parse the uploaded JSON data to load diagrams.

The uploader requires:

  • saveUrl property for receiving and storing uploaded files
  • removeUrl property for handling file deletion operations
  • File parsing logic to extract JSON data from uploaded files
import { ViewChild, Component, ViewEncapsulation  } from '@angular/core';
import { DiagramComponent, DiagramModule } from '@syncfusion/ej2-angular-diagrams';
import { Uploader, FileInfo } from '@syncfusion/ej2-inputs';
import { NodeModel } from '@syncfusion/ej2-diagrams';

@Component({
  imports: [DiagramModule],

  providers: [],
  standalone: true,
  selector: 'app-container',
  template: ` <div> <input id="fileupload" type="file" />
  <button (click)="onSaveClick()">Save Diagram</button> </div>
  <ejs-diagram #diagram id="diagram" width="100%" height="600px" [nodes]="nodes" > </ejs-diagram>`,
  encapsulation: ViewEncapsulation.None,
})
export class AppComponent {
  @ViewChild("diagram")
  public diagram!: DiagramComponent;
  public uploadObject: Uploader;
  public nodes: NodeModel[] = [];
  constructor() {
    this.uploadObject = new Uploader({
      asyncSettings: {
        saveUrl:
          'https://services.syncfusion.com/js/production/api/FileUploader/Save',
        removeUrl:
          'https://services.syncfusion.com/js/production/api/FileUploader/Remove',
      },
      success: this.onUploadSuccess.bind(this),
    });
  }
  ngOnInit() {
    // Initialize nodes
    this.nodes = [
    {
      id: 'Start',
      width: 140,
      height: 50,
      offsetX: 300,
      offsetY: 50,
      annotations: [{ id: 'label1', content: 'Start' }],
      shape: { type: 'Flow', shape: 'Terminator' },
    },
  ];
}

  ngAfterViewInit() {
    this.uploadObject.appendTo('#fileupload');
  }

  onUploadSuccess(args: { file: FileInfo }) {
    const file: any = args.file.rawFile;
    const reader = new FileReader();
    reader.readAsText(file);
    reader.onloadend = this.loadDiagram.bind(this);
  }

  loadDiagram(event: ProgressEvent<FileReader>) {
    const jsonString = event.target?.result as string;
    this.diagram.loadDiagram(jsonString);
  }

  download(data: string): void {
    if ((window.navigator as any).msSaveBlob) {
      const blob: any = new Blob([data], {
        type: 'data:text/json;charset=utf-8,',
      });
      (window.navigator as any).msSaveBlob(blob, 'Diagram.json');
    } else {
      const dataStr: string =
        'data:text/json;charset=utf-8,' + encodeURIComponent(data);
      const a: HTMLAnchorElement = document.createElement('a');
      a.href = dataStr;
      a.download = 'Diagram.json';
      document.body.appendChild(a);
      a.click();
      a.remove();
    }
  }

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

Mermaid Syntax Integration

Overview

The Diagram component supports importing and exporting diagrams using Mermaid syntax. Mermaid is a markdown-inspired syntax for creating diagrams programmatically, enabling easy diagram creation and sharing across different platforms.

This functionality supports:

  • Mind maps
  • Flowcharts
  • UML sequence diagrams

Saving Diagrams as Mermaid Syntax

The saveDiagramAsMermaid method converts compatible diagrams into Mermaid syntax format. This method works specifically with Flowchart and Mind map layouts.

//returns the serialized Mermaid string of the Diagram
data = this.diagram.saveDiagramAsMermaid();

Loading Diagrams from Mermaid Syntax

The loadDiagramFromMermaid method creates diagrams from Mermaid syntax data, automatically generating the appropriate layout and styling.

Loading Flowchart Layout

The following example demonstrates loading a flowchart diagram from Mermaid syntax:

import { Component, ViewChild, ViewEncapsulation } from '@angular/core';
import { DiagramComponent, DiagramModule } from '@syncfusion/ej2-angular-diagrams';
import { Diagram, NodeModel, ConnectorModel, FlowShapeModel, FlowchartLayout } from '@syncfusion/ej2-diagrams';

Diagram.Inject(FlowchartLayout);

@Component({
  imports: [
    DiagramModule
  ],

  providers: [],
  standalone: true,
  selector: "app-container",
  template: `
  <button (click)="loadMermaidFlowchart()">Load</button>
  <ejs-diagram #diagram id="diagram" width="100%" height="600px" [getNodeDefaults]="getNodeDefaults"
  [getConnectorDefaults]="getConnectorDefaults" [layout]="layout"> </ejs-diagram>`,
  encapsulation: ViewEncapsulation.None
})

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

  public getNodeDefaults(node: NodeModel): NodeModel {
    node.width = 120;
    node.height = 50;
    if ((node.shape as FlowShapeModel).shape === 'Decision') {
      node.height = 80;
    }

    return node;
  }

  public getConnectorDefaults(connector: ConnectorModel): ConnectorModel {
    connector.type = 'Orthogonal';
    return connector;
  }

  public layout = { type: 'Flowchart' }

  public loadMermaidFlowchart() {
    const mermaidFlowchartData = `flowchart TD
        A[Start] --> B(Process)
        B -.- C{Decision}
        C --Yes--> D[Plan 1]
        C ==>|No| E[Plan 2]
        style A fill:#90EE90,stroke:#333,stroke-width:2px;
        style B fill:#4682B4,stroke:#333,stroke-width:2px;
        style C fill:#FFD700,stroke:#333,stroke-width:2px;
        style D fill:#FF6347,stroke:#333,stroke-width:2px;
        style E fill:#FF6347,stroke:#333,stroke-width:2px;`;

    // load the mermaid flowchart data to diagram
    //parameter: mermaidFlowchartData - mermaid format string data for flowchart
    this.diagram.loadDiagramFromMermaid(mermaidFlowchartData);
  }

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

Loading Mind Map Layout

The following example demonstrates loading a mind map diagram from Mermaid syntax:

import { Component, ViewChild, ViewEncapsulation } from '@angular/core';
import { DiagramComponent, DiagramModule } from '@syncfusion/ej2-angular-diagrams';
import { Diagram, NodeModel, ConnectorModel, TextStyleModel, ShapeAnnotationModel,
  DecoratorModel, StrokeStyleModel, MindMap } from '@syncfusion/ej2-diagrams';

Diagram.Inject(MindMap);

@Component({
  imports: [
    DiagramModule
  ],

  providers: [],
  standalone: true,
  selector: "app-container",
  template: `
  <button (click)="loadMermaidMindmap()">Load</button>
  <ejs-diagram #diagram id="diagram" width="100%" height="600px" [getNodeDefaults]="getNodeDefaults"
  [getConnectorDefaults]="getConnectorDefaults" [layout]="layout"> </ejs-diagram>`,
  encapsulation: ViewEncapsulation.None
})

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

  public getNodeDefaults(node: NodeModel): NodeModel {
    node.width = 120;
    node.height = 50;
    return node;
  }

  public getConnectorDefaults(connector: ConnectorModel): ConnectorModel {
    connector.type = 'Orthogonal';
    (connector.targetDecorator as DecoratorModel).shape = 'None';
    return connector;
  }

  public layout = {
    type: 'MindMap',
    verticalSpacing: 50,
    horizontalSpacing: 50,
    orientation: 'Horizontal',
  }

  public loadMermaidMindmap() {
    const mermaidMindmapData = `mindmap
                            root((mindmap))
                              Origins
                                Popularisation
                              Research
                                On effectivness<br/>and features
                                On Automatic creation
                              Tools
                                Pen and paper
                                Mermaid `;

    // load the mermaid mindmap data to diagram
    //parameter: mermaidMindmapData - mermaid format string data for mindmap
    this.diagram.loadDiagramFromMermaid(mermaidMindmapData);
  }

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

Loading UML Sequence Diagram

The following example demonstrates loading a UML Sequence diagram from Mermaid syntax:

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

@Component({
  imports: [DiagramModule],
  providers: [],
  standalone: true,
  selector: "app-container",
  template: `
  <button (click)="loadMermaidSeqDiagram()">Load Mermaid</button>
  <ejs-diagram #diagram id="diagram" width="100%" height="600px"></ejs-diagram>`,
  encapsulation: ViewEncapsulation.None
})

export class AppComponent {
  @ViewChild("diagram")
  public diagram!: DiagramComponent;
  public loadMermaidSeqDiagram() {
    const mermaidData: string = `
 sequenceDiagram
    participant User
    participant Controller
    participant Service
    participant Database

    User->>Controller: sendRequest()
    activate Controller

    Controller->>Service: processRequest()
    activate Service

    Service->>Database: queryData()
    activate Database
    Database-->>Service: returnData()
    deactivate Database

    Service-->>Controller: returnResponse()
    deactivate Service

    Controller-->>User: sendResponse()
    deactivate Controller`;

    // load the mermaid sequence diagram data to diagram
    //parameter: mermaidData - mermaid format string data for UML Sequence Diagram
    this.diagram.loadDiagramFromMermaid(mermaidData);
  }

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

NOTE

Mermaid syntax-based serialization and deserialization supports only Flowchart layout, Mind map layout, and UML Sequence Diagram. Ensure that your Mermaid data aligns with one of these supported layouts for successful diagram loading.