references/bpmn-diagrams.md
# BPMN Diagrams
## Table of Contents
- [Overview](#overview)
- [BPMN Setup](#bpmn-setup)
- [BPMN Shapes](#bpmn-shapes)
- [Activities](#activities)
- [Events](#events)
- [Gateways](#gateways)
- [Flows](#flows)
- [Data Elements](#data-elements)
- [Groups and Annotations](#groups-and-annotations)
## Overview
**BPMN (Business Process Model and Notation)** is a standardized language for modeling business processes.
Syncfusion provides built-in BPMN shape libraries and styling.
### BPMN Concepts
- **Activities** - Work performed (tasks, subprocesses)
- **Events** - Things that happen (start, end, intermediate)
- **Gateways** - Decision points that route flow
- **Flows** - Connections between elements (sequence flow, message flow, association)
- **Data** - Objects and sources used in the process
- **Groups** - Logical grouping of elements
---
## BPMN Setup
### Enable BPMN Module
```typescript
import { Component } from '@angular/core';
import {
Diagram,
DiagramModule,
} from '@syncfusion/ej2-angular-diagrams';
Diagram.Inject(BpmnDiagrams);
import { BpmnDiagrams } from '@syncfusion/ej2-angular-diagrams';
@Component({
selector: 'app-bpmn',
template: `<ejs-diagram #diagram id="diagram" width="100%" height="500px"></ejs-diagram>`,
standalone:true,
imports:[DiagramModule]
})
export class BpmnComponent {}
```
### Basic BPMN Diagram
```typescript
nodes = [
// Start event
{
id: 'start',
width: 50,
height: 50,
offsetX: 100,
offsetY: 100,
shape: { type: 'Bpmn', shape: 'Event', event: { event: 'Start' } }
},
// Task
{
id: 'task1',
width: 100,
height: 80,
offsetX: 250,
offsetY: 100,
shape: { type: 'Bpmn', shape: 'Activity', activity: { activity: 'Task' } } // Sets the type of the task as task: {type: 'Service'}
},
// End event
{
id: 'end',
width: 50,
height: 50,
offsetX: 400,
offsetY: 100,
shape: { type: 'Bpmn', shape: 'Event', event: { event: 'End' } }
}
];
connectors = [
{ id: 'conn1', sourceID: 'start', targetID: 'task1' },
{ id: 'conn2', sourceID: 'task1', targetID: 'end' }
];
```
---
## BPMN Shapes
### Shape Structure
```typescript
shape: {
type: 'Bpmn',
shape: 'Activity' | 'Event' | 'Gateway' | 'Flow' | 'DataObject' | 'DataSource' | 'Group',
// Use the correct sub-property for each BPMN type:
// - activity: { activity: 'Task' | 'SubProcess' | ... }
// - event: { event: 'Start' | 'End' | 'Intermediate', trigger: ... }
// - gateway: { type: 'Exclusive' | 'Parallel' | ... }
// - dataObject: { ... }
// - textAnnotation: { ... }
}
```
---
## Activities
Activities represent work performed in the process:
### Basic Task
```typescript
{
id: 'task1',
width: 100,
height: 80,
offsetX: 250,
offsetY: 100,
shape: { type: 'Bpmn', shape: 'Activity', activity: { activity: 'Task' } } //Sets the type of the task as Sendtask: {type: 'Service'}
}
```
### Subprocess
A task containing other activities (collapsed):
```typescript
{
id: 'subprocess',
width: 100,
height: 80,
shape: { type: 'Bpmn', shape: 'Activity', activity: { activity: 'SubProcess' } }
}
```
### Expanded Subprocess
Subprocess showing inner activities:
```typescript
{
id: 'expandedSubprocess',
width: 300,
height: 200,
shape: { type: 'Bpmn', shape: 'Activity', activity: { activity: 'SubProcess', collapsed: false } }
// Contains child nodes inside
}
```
### Activity Types
```typescript
// Use the activity property:
activity: { activity: 'Task' } // Basic work
activity: { activity: 'SubProcess', collapsed: true } // Collapsed subprocess
activity: { activity: 'SubProcess', collapsed: false } // Expanded subprocess
activity: { activity: 'Transaction' } // Task with compensation
activity: { activity: 'Task', loop: 'Standard' } // Repeating activity
activity: { activity: 'Task', loop: 'ParallelMultiInstance' } // Parallel instances
activity: { activity: 'Task', loop: 'SequentialMultiInstance' } // Sequential instances
```
### Activity Markers
```typescript
{
id: 'task1',
shape: {
type: 'Bpmn',
shape: 'Activity',
activity: {
activity: 'Task',
subProcess: {
collapsed: true, // Show + or - marker
type: 'Default', // Type of subprocess
adhoc: false, // Ad-hoc subprocess
compensation: false // Compensation activity
},
loop: 'None' // None, Standard, ParallelMultiInstance, SequentialMultiInstance
}
}
}
```
---
## Events
Events represent occurrences in the process:
### Start Events
```typescript
{
id: 'start',
width: 50,
height: 50,
shape: { type: 'Bpmn', shape: 'Event', event: { event: 'Start' } }
}
```
### End Events
```typescript
{
id: 'end',
width: 50,
height: 50,
shape: { type: 'Bpmn', shape: 'Event', event: { event: 'End' } }
}
```
### Intermediate Events
Occur during the process:
```typescript
{
id: 'intermediate',
width: 50,
height: 50,
shape: { type: 'Bpmn', shape: 'Event', event: { event: 'Intermediate' } }
}
```
### Event Types
Within each event, specify the trigger:
```typescript
event: {
trigger: 'None' | 'Message' | 'Timer' | 'Escalation' | 'Link' | 'Error' | 'Compensation' | 'Signal' | 'Multiple' | 'ParallelMultiInstance' | 'Conditional' | 'Terminate'
}
// Examples:
{
id: 'messageStart',
shape: {
type: 'Bpmn',
shape: 'Event',
event: { event: 'Start', trigger: 'Message' }
}
}
{
id: 'timerIntermediate',
shape: {
type: 'Bpmn',
shape: 'Event',
event: { event: 'Intermediate', trigger: 'Timer' }
}
}
```
### Event Direction
```typescript
event: {
trigger: 'Message',
event: 'Intermediate',
type: 'Catching' // Catching (default) or Throwing
}
```
---
## Gateways
Gateways control flow routing:
### Exclusive Gateway (XOR)
Only one path is taken:
```typescript
{
id: 'decision',
width: 50,
height: 50,
shape: { type: 'Bpmn', shape: 'Gateway', gateway: { type: 'Exclusive' } }
}
```
### Parallel Gateway
All paths are executed:
```typescript
{
id: 'parallel',
width: 50,
height: 50,
shape: { type: 'Bpmn', shape: 'Gateway', gateway: { type: 'Parallel' } }
}
```
### Inclusive Gateway
One or more paths:
```typescript
{
id: 'inclusive',
width: 50,
height: 50,
shape: { type: 'Bpmn', shape: 'Gateway', gateway: { type: 'Inclusive' } }
}
```
### Complex Gateway
Complex branching logic:
```typescript
{
id: 'complex',
width: 50,
height: 50,
shape: { type: 'Bpmn', shape: 'Gateway', gateway: { type: 'Complex' } }
}
```
### Event-based Gateway
Routes based on events:
```typescript
{
id: 'eventBased',
width: 50,
height: 50,
shape: { type: 'Bpmn', shape: 'Gateway', gateway: { type: 'EventBased' } }
}
```
---
## Flows
Flows are connectors with BPMN semantics:
### Sequence Flow
Normal flow between activities:
```typescript
{
id: 'flow1',
sourceID: 'task1',
targetID: 'task2',
shape: {
type: 'Bpmn',
flow: 'Sequence'
}
}
```
### Message Flow
Communication between pools/lanes:
```typescript
{
id: 'messageFlow',
sourceID: 'task1',
targetID: 'task2',
shape: {
type: 'Bpmn',
flow: 'Message'
}
}
```
### Association
Links annotations/gateways to activities:
```typescript
{
id: 'association',
sourceID: 'annotation',
targetID: 'task1',
shape: {
type: 'Bpmn',
flow: 'Association'
}
}
```
---
## Data Elements
### Data Object
Represents data used in process:
```typescript
{
id: 'dataObject',
width: 50,
height: 70,
shape: {
type: 'Bpmn',
shape: 'DataObject',
dataObject: {
collection: false, // Is it a collection?
type: 'None' // Type of data
}
}
}
```
### Data Object Reference
Points to a data object:
```typescript
{
id: 'dataRef',
width: 50,
height: 70,
shape: {
type: 'Bpmn',
shape: 'DataObject',
dataObject: {
collection: true,
type: 'Input' // or 'Output'
}
}
}
```
### Data Source
External data source:
```typescript
{
id: 'datasource',
width: 50,
height: 70,
shape: {
type: 'Bpmn',
shape: 'DataSource'
}
}
```
---
## Groups and Annotations
### Group
Logical grouping of elements:
```typescript
{
id: 'group',
width: 400,
height: 300,
shape: {
type: 'Bpmn',
shape: 'Group'
}
// Contains child nodes inside
}
```
### Text Annotation
Adds documentation:
```typescript
{
id: 'annotation',
width: 100,
height: 50,
annotations: [{
content: 'This is a note about the process'
}],
shape: {
type: 'Bpmn',
shape: 'TextAnnotation',
}
}
```
---
## Troubleshooting BPMN Common Issues
### ❌ BPMN Shapes Not Rendering
**Problem:** BPMN shapes appear as basic rectangles instead of BPMN-specific shapes.
**Solution:** Ensure BpmnDiagrams module is injected:
```typescript
import { Diagram, BpmnDiagrams } from '@syncfusion/ej2-angular-diagrams';
Diagram.Inject(BpmnDiagrams);
```
### ❌ Gateway Logic Not Working
**Problem:** Sequence flows not routing correctly through gateways.
**Solution:** Use proper connector configuration with gateway types:
```typescript
// Define gateway correctly
{
id: 'xor_gate',
shape: { type: 'Bpmn', shape: 'Gateway', gateway: { type: 'Exclusive' } }
}
// Use labeled connectors for decision paths
connectors = [
{
id: 'yes_path',
sourceID: 'xor_gate',
targetID: 'success_task',
annotations: [{ content: 'Yes' }]
},
{
id: 'no_path',
sourceID: 'xor_gate',
targetID: 'reject_task',
annotations: [{ content: 'No' }]
}
];
```
### ❌ Export Not Working
**Problem:** Export fails or produces blank images.
**Solution:** Ensure diagram content is fully loaded before exporting:
```typescript
// Wait for diagram to initialize
ngAfterViewInit() {
setTimeout(() => {
this.diagram.exportDiagram({format: 'PNG', fileName: 'bpmn-process'});
}, 500);
}
```
### ⚠️ BPMN Best Practices
1. **Always inject BpmnDiagrams** at component level - not using it breaks rendering
2. **Use Swimlane module** for department/role separation in complex workflows
3. **Label all decision paths** with 'Yes'/'No' or condition names
4. **Group related activities** using Groups or SubProcess shapes
5. **Use consistent naming** for events (Start_ApplicationReview, End_Approved, etc.)
6. **Test with swimlanes early** - adding swimlanes to existing nodes requires restructuring
---
**→ Next: Create [UML diagrams](uml-diagrams.md) for system design**
references/connectors.md
# Connectors
## Table of Contents
- [Overview](#overview)
- [Connector Types](#connector-types)
- [Creating Connectors](#creating-connectors)
- [Segments Configuration](#segments-configuration)
- [Source and Target](#source-and-target)
- [Multiple Segments](#multiple-segments)
- [Bezier Control Points](#bezier-control-points)
- [Connector Customization](#connector-customization)
- [Connector Events](#connector-events)
- [getConnectorDefaults Pattern](#getconnectordefaults-pattern)
## Overview
**Connectors** link nodes together and represent relationships, flows, or data paths in a diagram.
## Connector Types
### 1. Straight Connector
Direct line between two nodes:
```typescript
{
id: 'connector1',
sourceID: 'node1',
targetID: 'node2',
type: 'Straight'
}
```
**Use cases:**
- Simple process flows
- Minimal connections
- Clean diagrams
### 2. Orthogonal Connector
Right-angle turns (horizontal and vertical):
```typescript
{
id: 'connector1',
sourceID: 'node1',
targetID: 'node2',
type: 'Orthogonal'
}
```
**Use cases:**
- Flowcharts (avoids line confusion)
- Circuit diagrams
- Structured layouts
### 3. Bezier Connector
Smooth curved lines:
```typescript
{
id: 'connector1',
sourceID: 'node1',
targetID: 'node2',
type: 'Bezier'
}
```
**Use cases:**
- Organic diagrams
- Mind maps
- Artistic or visual workflows
---
## Creating Connectors
### Add Connectors to Diagram
```typescript
connectors = [
{
id: 'connector1',
sourceID: 'node1',
targetID: 'node2',
type: 'Orthogonal'
},
{
id: 'connector2',
sourceID: 'node2',
targetID: 'node3',
type: 'Straight'
}
];
<ejs-diagram [connectors]="connectors"></ejs-diagram>
```
### Add Connectors Dynamically
```typescript
diagram.add({
id: 'newConnector',
sourceID: 'node1',
targetID: 'node2',
type: 'Orthogonal'
});
// Add multiple
const connectorArray = [conn1, conn2, conn3];
diagram.addElements(connectorArray);
```
### Remove Connectors
```typescript
diagram.remove(diagram.connectors[0]);
```
---
## Segments Configuration
### What Are Segments?
Segments define **how the connector path is drawn**:
```typescript
{
id: 'connector1',
sourceID: 'node1',
targetID: 'node2',
type: 'Orthogonal',
segments: [
{
type: 'Orthogonal',
length: 100,
direction: 'Right'
},
{
type: 'Orthogonal',
direction: 'Bottom'
}
]
}
```
### Segment Directions
For **Orthogonal** type:
- `Right` - Move right (East)
- `Left` - Move left (West)
- `Top` - Move up (North)
- `Bottom` - Move down (South)
### Segment Types
```typescript
{
type: 'Orthogonal', // Right angles
direction: 'Right',
length: 100
}
{
type: 'Bezier', // Smooth curve
vector1: { distance: 100, angle: 90 }
}
```
### Auto-routing Segments
Let the diagram calculate segments automatically:
```typescript
{
id: 'connector1',
sourceID: 'node1',
targetID: 'node2',
type: 'Orthogonal'
// No segments specified - auto-routed
}
```
---
## Source and Target
### Link by Node ID
```typescript
{
id: 'connector1',
sourceID: 'node1', // Start from node1
targetID: 'node2' // End at node2
}
```
### Link to Specific Ports
```typescript
{
id: 'connector1',
sourceID: 'node1',
sourcePortID: 'port1', // Start port
targetID: 'node2',
targetPortID: 'port2' // End port
}
```
See [ports.md](ports.md) for detailed port configuration.
### Dynamic Source/Target
```typescript
// Change connection at runtime
diagram.connectors[0].sourceID = 'newNode1';
diagram.connectors[0].targetID = 'newNode2';
diagram.dataBind();
```
---
## Multiple Segments
### Chain Multiple Paths
```typescript
{
id: 'connector1',
sourceID: 'node1',
targetID: 'node2',
type: 'Orthogonal',
segments: [
{ type: 'Orthogonal', length: 100, direction: 'Right' },
{ type: 'Orthogonal', length: 50, direction: 'Bottom' },
{ type: 'Orthogonal', direction: 'Right' }
]
}
```
### Custom Waypoints
```typescript
{
id: 'connector1',
sourceID: 'node1',
targetID: 'node2',
type: 'Straight',
segments: [
{
type: 'Straight',
points: [
{ x: 100, y: 100 },
{ x: 200, y: 150 },
{ x: 300, y: 100 }
]
}
]
}
```
---
## Bezier Control Points
### What Are Control Points?
Bezier curves use **control points** to define curve shape:
```typescript
{
id: 'connector1',
sourceID: 'node1',
targetID: 'node2',
type: 'Bezier',
segments: [
{
type: 'Bezier',
vector1: { distance: 100, angle: 90}, // First control point
vector2: { distance: 45, angle: 270 } // Second control point
}
]
}
```
### Control Point Orientation
**vector1** - Control point relative to **source**
**vector2** - Control point relative to **target**
Higher values = more pronounced curve
## Connector Customization
### Styling
```typescript
{
id: 'connector1',
sourceID: 'node1',
targetID: 'node2',
style: {
strokeColor: '#FF0000', // Red line
strokeWidth: 2,
strokeDashArray: '5,5' // Dashed line
}
}
```
### Arrow Heads (Decorators)
```typescript
{
id: 'connector1',
sourceID: 'node1',
targetID: 'node2',
sourceDecorator: {
shape: 'Arrow',
width: 10,
height: 10,
style: { fill: '#000000' }
},
targetDecorator: {
shape: 'Arrow',
width: 10,
height: 10,
style: { fill: '#000000' }
}
}
```
**Decorator shapes:** Arrow, Circle, Diamond, OpenArrow, Fletch, OpenFetch, Crescent
### Labels on Connectors
```typescript
{
id: 'connector1',
sourceID: 'node1',
targetID: 'node2',
annotations: [
{
content: 'Approves',
offset: 0.5, // Middle of connector
style: { fontSize: 12, fill: 'black' }
}
]
}
```
See [labels-and-annotations.md](labels-and-annotations.md) for details.
---
## Connector Events
### Selection Events
```typescript
diagram.selectionChange((args) => {
if (args.state === 'Changed') {
console.log('selectionChange');
//Customize
}
});
```
### Connection Changed
```typescript
diagram.connectionChange((args) => {
if (args.state === 'Changed') {
console.log('connectionChange');
//Customize
}
});
```
---
## getConnectorDefaults Pattern
### Purpose
Set default styles for all connectors:
```typescript
getConnectorDefaults = (connector: ConnectorModel): ConnectorModel => {
return {
type: 'Orthogonal',
style: {
strokeColor: '#333333',
strokeWidth: 2
},
targetDecorator: {
shape: 'Arrow',
width: 10,
height: 10
}
};
};
<ejs-diagram [getConnectorDefaults]="getConnectorDefaults"></ejs-diagram>
```
### Conditional Defaults
```typescript
getConnectorDefaults = (connector: ConnectorModel): ConnectorModel => {
const isApproval = connector.id.includes('approve');
const isDenial = connector.id.includes('deny');
return {
type: 'Orthogonal',
style: {
strokeColor: isApproval ? '#00AA00' : isDenial ? '#FF0000' : '#333333',
strokeWidth: 2,
strokeDashArray: isDenial ? '5,5' : ''
},
targetDecorator: {
shape: 'Arrow'
}
};
};
```
---
**→ Next: Add [labels and annotations](labels-and-annotations.md) to your diagram**
references/data-binding.md
# Data Binding
## Overview
**Data Binding** renders diagrams from structured data (JSON, arrays, databases) instead of manually defining each node.
### Use Cases
- **Organizational charts** from employee data
- **Dependency graphs** from project data
- **Process diagrams** from workflow definitions
---
## DataManager Setup
### Basic DataSourceSettings
```typescript
let dataSource = [
{ id: 'node1', parentId: null, label: 'Node 1' },
{ id: 'node2', parentId: 'node1', label: 'Node 2' },
{ id: 'node3', parentId: 'node1', label: 'Node 3' }
]
@Component({
template: `
<ejs-diagram
width="1000px" height="600px"
[dataSourceSettings]="dataSourceSettings"
[getNodeDefaults]="getNodeDefaults"
[getConnectorDefaults]="getConnectorDefaults">
</ejs-diagram>
`
})
export class DataBoundDiagramComponent {
dataSourceSettings = {
id: 'id', // Property name for node ID
parentId: 'parentId', // Property name for parent relationship
dataSource: new DataManager(dataSource)
};
getNodeDefaults = (node: NodeModel): NodeModel => {
node.width = 100;
node.height = 80;
node.shape = { type: 'Flow', shape: 'Process' };
node.annotations = [{ content: (node as any).label }];
return node;
};
getConnectorDefaults = (connector: ConnectorModel): ConnectorModel => {
connector.type = 'Orthogonal';
return connector;
};
}
```
---
## Data Mapping
### Property Mapping
```typescript
dataSourceSettings = {
id: 'id', // Node ID field
parentId: 'parentId', // Parent ID field
dataSource: new DataManager(employeeData),
}
```
### Custom Mapping
```typescript
let dataSource = [
{ nodeId: 'emp1', managerId: null, name: 'CEO' },
{ nodeId: 'emp2', managerId: 'emp1', name: 'Manager' }
],
let dataSourceSettings = {
id: 'nodeId', // Maps to nodeId field
parentId: 'managerId', // Maps to managerId field
dataSource: new DataManager(dataSource)
}
```
---
## Node Template
### setNodeTemplate
Define how data renders as nodes:
```typescript
setNodeTemplate = (node: NodeModel): Container => {
// Create an outer StackPanel as container to contain image and text elements
let container = new StackPanel();
container.width = 200;
container.height = 60;
container.cornerRadius = 10;
container.style.fill = 'skyblue';
container.horizontalAlignment = 'Left';
container.orientation = 'Horizontal';
container.id = (node.data as any).Name + '_StackContainer';
// Create an inner image element to displaying image
let innerContent = new ImageElement();
innerContent.id = (node.data as any).Name + '_innerContent';
innerContent.width = 40;
innerContent.height = 40;
innerContent.margin.left = 20;
innerContent.style.fill = 'lightgrey';
// Create a inner text element for displaying employee details
let text = new TextElement();
text.content = 'Name: ' + (node.data as any).Name;
text.margin = { left: 10, top: 5 };
text.id = (node.data as any).Name + '_textContent';
text.style.fill = 'green';
text.style.color = 'white';
if ((node.data as any).Name === 'Steve-Ceo') {
text.style.fill = 'black';
text.style.color = 'white';
}
// Add inner image and text element to the outer StackPanel
container.children = [innerContent, text];
return container;
};
// In template
<ejs-diagram [setNodeTemplate]="setNodeTemplate"></ejs-diagram>
```
---
## Organizational Chart from Data
### Data Structure
```typescript
employees = [
{ id: 'ceo', parentId: null, name: 'John Smith', role: 'CEO', dept: 'Executive' },
{ id: 'cto', parentId: 'ceo', name: 'Sarah Johnson', role: 'CTO', dept: 'Engineering' },
{ id: 'dev1', parentId: 'cto', name: 'Mike Davis', role: 'Developer', dept: 'Engineering' },
{ id: 'dev2', parentId: 'cto', name: 'Lisa Brown', role: 'Developer', dept: 'Engineering' },
{ id: 'hr', parentId: 'ceo', name: 'Amy Wilson', role: 'HR Manager', dept: 'HR' }
];
```
### Org-Chart Component
```typescript
import { HierarchicalTreeService, DataBindingService } from '@syncfusion/ej2-angular-diagrams';
@Component({
selector: 'app-org-chart',
providers: [HierarchicalTreeService, DataBindingService]
template: `
<ejs-diagram
[layout]="layout"
[dataSourceSettings]="dataSourceSettings"
[getNodeDefaults]="getNodeDefaults">
</ejs-diagram>
`,
})
export class OrgChartComponent {
layout = {
type: 'OrganizationalChart',
horizontalSpacing: 100,
verticalSpacing: 80
};
dataSourceSettings = {
id: 'id',
parentId: 'parentId',
dataSource: new DataManager(employees)
};
getNodeDefaults = (node: NodeModel): NodeModel => {
const data: any = node.data || {};
node.width = 150;
node.height = 80;
node.shape = { type: 'Flow', shape: 'Process' };
node.style = {
fill: this.getColorByDept(data.dept),
strokeColor: '#333333'
};
node.annotations = [
{ content: data.name, style: { fontSize: 12, bold: true } },
{ content: data.role, offset: { x: 0.5, y: 0.6 }, style: { fontSize: 10 } }
];
return node;
};
getColorByDept = (dept: string): string => {
const colors: Record<string, string> = {
Executive: '#FFD700',
Engineering: '#90EE90',
HR: '#FFB6C1'
};
return colors[dept] || '#CCCCCC';
};
}
```
---
## Remote Data Source
```typescript
import { Component, ViewEncapsulation } from '@angular/core';
import { Diagram, NodeModel, DiagramTools, SnapSettingsModel, SnapConstraints } from '@syncfusion/ej2-diagrams';
import { DataManager } from '@syncfusion/ej2-data';
import { DataBindingService, DiagramComponent, DiagramModule, HierarchicalTreeService } from '@syncfusion/ej2-angular-diagrams';
@Component({
imports: [
DiagramModule
],
providers: [HierarchicalTreeService, DataBindingService],
standalone: true,
selector: 'app-container',
template: `<ejs-diagram #diagram id="diagram" width="100%" height="580px" [snapSettings]='snapSettings' [getConnectorDefaults]='connDefaults' [getNodeDefaults]='nodeDefaults' [tool]='tool' [layout]='layout' [dataSourceSettings]='data1' >
</ejs-diagram>`,
encapsulation: ViewEncapsulation.None
})
export class AppComponent {
public diagram?: DiagramComponent;
public nodeDefaults(node: NodeModel): NodeModel {
node.width = 80;
node.height = 40;
node.shape = { type: 'Basic', shape: 'Rectangle' };
node.style = { fill: '#048785', strokeColor: 'Transparent' };
return node;
};
public data1: Object = {
id: 'Id', parentId: 'ParentId',
dataSource: new DataManager(
{ url: 'https://services.syncfusion.com/js/production/api/RemoteData', crossDomain: true },
),
//binds the external data with node
doBinding: (nodeModel: NodeModel, data: DataInfo, diagram: Diagram) => {
nodeModel.annotations = [{
/* tslint:disable:no-string-literal */
content: data['Label'],
style: { color: 'white' }
}];
}
};
public connDefaults(connector: any): void {
connector.type = 'Orthogonal';
connector.style.strokeColor = '#048785';
connector.targetDecorator.shape = 'None';
};
public tool: DiagramTools = DiagramTools.ZoomPan;
public snapSettings: SnapSettingsModel = { constraints: SnapConstraints.None };
public layout: Object = {
type: 'HierarchicalTree', margin: { left: 0, right: 0, top: 100, bottom: 0 },
verticalSpacing: 40,
};
}
export interface DataInfo {
[key: string]: string;
}
```
---
## Dynamic Data Updates
### Refresh Diagram with New Data
```typescript
updateDataSource = (newData: any[]) => {
this.dataSourceSettings.dataSource = new DataManager(newData);
this.diagram.dataBind();
this.diagram.doLayout();
};
// Usage
updateDataSource(updatedEmployeeList);
```
## Troubleshooting Data Binding
### ❌ Nodes Not Rendering from Data
**Problem:** Data array defined but nodes don't appear.
**Solution:** Ensure `dataSourceSettings` is configured and property names match data:
```typescript
// ❌ WRONG - Missing dataSourceSettings
template: `<ejs-diagram [nodes]="employeeData"></ejs-diagram>`
// ✅ CORRECT - Use dataSourceSettings
template: `
<ejs-diagram
[dataSourceSettings]="dataSourceSettings"
[getNodeDefaults]="getNodeDefaults">
</ejs-diagram>
`
dataSourceSettings = {
id: 'EmployeeID', // ← Must match your data field name
parentId: 'ReportingTo', // ← Must match your data field name
dataSource: new DataManager(employees)
};
```
### ❌ Data Context Lost in Template
**Problem:** `setNodeTemplate` can't access data properties.
**Solution:** Properly destructure and pass data through node object:
```typescript
// ❌ WRONG - Data not accessible
setNodeTemplate = (node: any): NodeModel => ({
id: node.id,
annotations: [{ content: node.name }] // This is undefined!
})
// ✅ CORRECT - Access data via the full node object
setNodeTemplate = (node: any): NodeModel => {
const data = node.data || node; // Data may be nested
return {
id: data.EmployeeID,
width: 120,
height: 80,
annotations: [{
content: `${data.EmployeeName}\n${data.Department}`
}]
};
}
```
### ❌ Hierarchical Layout Not Working
**Problem:** Nodes arranged randomly instead of hierarchical tree.
**Solution:** Set layout in diagram component:
```typescript
// ❌ WRONG - No layout specified
<ejs-diagram [dataSourceSettings]="dataSourceSettings"></ejs-diagram>
// ✅ CORRECT - Specify hierarchical layout
<ejs-diagram
[dataSourceSettings]="dataSourceSettings"
[layout]="layout">
</ejs-diagram>
layout = {
type: 'HierarchicalTree', // ← Activates tree layout
orientation: 'TopToBottom', // or LeftToRight, etc.
verticalSpacing: 60,
horizontalSpacing: 60
};
```
### ❌ Parent Nodes Missing
**Problem:** Root/top-level nodes don't appear as containers.
**Solution:** Ensure root nodes have `null` or empty `parentId`:
```typescript
dataSource: [
{ EmployeeID: 'emp1', ReportingTo: null, name: 'CEO' }, // ← null parentId
{ EmployeeID: 'emp2', ReportingTo: 'emp1', name: 'Manager' }, // ← Children
{ EmployeeID: 'emp3', ReportingTo: 'emp1', name: 'Manager' } // ← Children
]
```
### ⚠️ Data Binding Best Practices
1. **Match property names exactly** - `'id'` field in settings must match data field name
2. **Use `getNodeDefaults`** for consistent styling across all data-bound nodes
3. **Test with small dataset first** - generate org chart from 3-5 nodes before scaling
4. **Verify parent-child relationships** - circular references or invalid IDs break layout
5. **Use `setNodeTemplate`** only for rendering customization - don't add/remove nodes here
6. **Reload data carefully** - call `diagram.doLayout()` after updating `dataSourceSettings.dataSource`
---
**→ Next: Add interactivity with [interaction and tools](interaction-and-tools.md)**
references/diagram-settings.md
# Diagram Settings
## Table of Contents
- [Overview](#overview)
- [Layers](#layers)
- [Virtualization](#virtualization)
- [Grid Lines](#grid-lines)
- [Ruler](#ruler)
- [Scroll Settings](#scroll-settings)
- [Page Settings](#page-settings)
- [Tooltip](#tooltip)
- [Overview Panel](#overview-panel)
- [Localization](#localization)
- [Accessibility](#accessibility)
## Overview
**Diagram Settings** control appearance, performance, and behavior of the diagram canvas.
---
## Layers
**Layers** organize diagram elements by z-order and visibility.
### Define Layers
```typescript
// Define layers in the diagram
this.layers = [
{
id: 'layer1',
visible: true,
objects: ['node1', 'node2'],
lock: false
},
{
id: 'layer2',
visible: true,
objects: ['node3'],
lock: false
}
];
```
### Layer Operations
```typescript
// Add a layer at runtime
diagram.addLayer({ id: 'newLayer', visible: true, lock: false }, [node]);
// Remove a layer by ID
diagram.removeLayer('layer1');
// Move objects between layers
diagram.moveObjects(['node1'], 'layer2');
// Get the active layer
const activeLayer = diagram.getActiveLayer();
// Set the active layer
diagram.setActiveLayer('layer2');
// Bring a layer forward
diagram.bringLayerForward('layer1');
// Send a layer backward
diagram.sendLayerBackward('layer1');
// Clone a layer
diagram.cloneLayer('layer1');
```
### Layer Visibility and Lock
```typescript
// Set layer visibility
this.layers[0].visible = false;
// Lock/unlock layer
this.layers[0].lock = true; // Prevent editing
this.layers[0].lock = false; // Allow editing
```
---
## Virtualization
**Virtualization** improves performance for large diagrams by rendering only visible elements.
### Enable Virtualization
```typescript
diagram.constraints = DiagramConstraints.Default | DiagramConstraints.Virtualization;
```
### Virtualization Settings
```typescript
// No special pageSettings are required for virtualization.
// Just set the Virtualization constraint on the diagram:
diagram.constraints = DiagramConstraints.Default | DiagramConstraints.Virtualization;
```
### Benefits
- Handles 10,000+ nodes efficiently
- Reduces memory usage
- Smooth panning and zooming
- Automatic element culling (hide off-screen elements)
---
## Grid Lines
**Grid lines** help with alignment and snapping.
### Enable Grid
```typescript
diagram.snapSettings = {
constraints: SnapConstraints.ShowLines,
horizontalGridlines: { snapIntervals: [5] },
verticalGridlines: { snapIntervals: [5] }
};
```
### Grid Line Appearance
```typescript
diagram.snapSettings = {
constraints: SnapConstraints.ShowLines,
horizontalGridlines: {
lineColor: '#E0E0E0',
lineIntervals: [1, 9, 0.25, 9.75],
snapIntervals: [5]
},
verticalGridlines: {
lineColor: '#E0E0E0',
lineIntervals: [1, 9, 0.25, 9.75],
snapIntervals: [5]
}
};
```
### Grid Styles
```typescript
// Dots
diagram.snapSettings = {
gridType: 'Dots',
horizontalGridlines: { dotIntervals: [3, 20, 1, 20] },
verticalGridlines: { dotIntervals: [3, 20, 1, 20] },
constraints: SnapConstraints.ShowLines
};
// Lines (default)
diagram.snapSettings = {
gridType: 'Lines',
horizontalGridlines: { snapIntervals: [5] },
constraints: SnapConstraints.ShowLines
};
```
---
## Ruler
**Ruler** shows measurement guides.
### Enable Ruler
```typescript
diagram.rulerSettings = {
showRulers: true,
horizontalRuler: {
thickness: 30
},
verticalRuler: {
thickness: 30
}
};
```
### Ruler Configuration
```typescript
rulerSettings: {
showRulers: true,
horizontalRuler: {
thickness: 30,
interval: 5, // Major tick interval
segmentWidth: 50,
markerColor: '#000000'
},
verticalRuler: {
thickness: 30,
interval: 5,
segmentWidth: 50
}
}
```
---
## Scroll Settings
Control panning and scrolling behavior.
### Auto-scroll
```typescript
diagram.scrollSettings = {
canAutoScroll: true,
autoScrollBorder: { left: 15, right: 15, top: 15, bottom: 15 }
};
```
### Scroll Limits and Zoom
```typescript
diagram.scrollSettings = {
minZoom: 0.2,
maxZoom: 5.0,
currentZoom: 1.0
};
```
### Scroll Position
```typescript
// Get scroll position
const scrollX = diagram.scrollSettings.horizontalOffset;
const scrollY = diagram.scrollSettings.verticalOffset;
// Set scroll position
diagram.scrollSettings.horizontalOffset = 100;
diagram.scrollSettings.verticalOffset = 100;
diagram.dataBind();
```
---
## Page Settings
Configure page size and margins for printing/export.
### Page Size
```typescript
diagram.pageSettings = {
width: 816, // 8.5" × 11" (Letter)
height: 1056,
orientation: 'Portrait',
margin: {
left: 20,
top: 20,
right: 20,
bottom: 20
}
};
```
### Standard Sizes
```typescript
// Letter (8.5" × 11")
width: 816, height: 1056
// A4 (210mm × 297mm)
width: 794, height: 1123
// Tabloid (11" × 17")
width: 1056, height: 1632
// Legal (8.5" × 14")
width: 816, height: 1344
```
### Orientation
```typescript
orientation: 'Portrait' // Taller than wide
orientation: 'Landscape' // Wider than tall
```
### Fit to Page
```typescript
diagram.fitToPage(); // Scale diagram to fit page
```
---
## Tooltip
Show tooltips on hover.
### Enable Tooltips
```typescript
diagram.tooltip = {
content: 'Diagram tooltip',
position: 'TopCenter',
relativeMode: 'Object'
};
```
### Custom Tooltip for Node
```typescript
{
id: 'node1',
tooltip: {
content: 'This is Node 1',
position: 'TopCenter',
relativeMode: 'Object'
},
constraints: NodeConstraints.Default | NodeConstraints.Tooltip
}
```
### Show/Hide Tooltip Programmatically
```typescript
// Show tooltip
diagram.showTooltip(diagram.nodes[0]);
// Hide tooltip
diagram.hideTooltip(diagram.nodes[0]);
```
---
## Overview Panel
Minimap showing full diagram overview.
### Enable Overview
```typescript
<div style="display:flex">
<ejs-diagram #diagram id="diagram"></ejs-diagram>
<ejs-overview id="overview" [sourceID]="'diagram'" width="200" height="150"></ejs-overview>
</div>
```
---
## Localization
Support multiple languages.
### Set Language
```typescript
import { L10n, setCulture } from '@syncfusion/ej2-base';
L10n.load({
'fr': {
'diagram': {
'save': 'Enregistrer',
'undo': 'Annuler',
'redo': 'Rétablir'
}
},
'de-DE': {
'diagram': {
'save': 'Speichern',
'undo': 'Rückgängig',
'redo': 'Wiederherstellen'
}
}
});
// Use French
setCulture('fr');
```
---
## Accessibility
WCAG 2.1 compliance for accessible diagrams.
### ARIA Labels
```typescript
{
id: 'node1',
accessibility: {
description: 'Process node representing order validation',
role: 'img'
},
annotations: [{
content: 'Validate Order',
style: { color: 'black' }
}]
}
```
### Keyboard Navigation
Supported keys:
- Tab / Shift+Tab: Move focus
- Enter: Select
- Escape: Deselect
- Arrow Keys: Move selected element
- Ctrl+A/X/C/V/Z/Y: Select All, Cut, Copy, Paste, Undo, Redo
### Screen Reader Support
Diagram elements use ARIA attributes for screen readers. Use `ariaLabel` and `accessibility` properties as needed.
### High Contrast Mode
```typescript
// Apply high contrast theme
import '@syncfusion/ej2-angular-theme-default/styles/highcontrast.css';
```
### Color Accessibility
```typescript
// Ensure sufficient contrast
style: {
fill: '#FFFFFF', // Light background
strokeColor: '#000000', // Dark border
color: '#000000' // Dark text
}
// Avoid color-only information
annotations: [{
content: '✓ Approved', // Use symbols in addition to color
style: { color: 'green' }
}]
```
---
**→ Reference complete. See [SKILL.md](../SKILL.md) for navigation guide.**
references/entity-relationship-diagrams.md
# Entity Relationship Diagrams
## When to Use This Skill
Use this skill when you need to:
- **Design database schemas** and visualize entity structures
- **Manage relationships** between database entities/tables
- **Show cardinality** with Crow's Foot notation
- **Define constraints** (primary key, foreign key, Unique, NotNull)
- **Customize appearance** of ER diagrams with styling and colors
- **Modify fields** dynamically at runtime without recreating entities
- **Create visual documentation** for data models and database structures
**Common scenarios:**
- Building a schema visualization for an e-commerce app (Customer → Order → Payment)
- Documenting database design before development
- Training stakeholders on data relationships
- Modeling complex database structures with multiple entities
---
## Component Overview
Entity Relationship Diagrams (ER Diagrams) in Syncfusion Angular Diagram display database entities, their fields, constraints, and relationships visually.
**Key Components:**
- **ER Entity Nodes** (ErShapeModel): Represent database tables
- **ER Fields** (ErFieldModel): Represent columns with data types and constraints
- **ER Connectors** (ErConnectorShapeModel): Show relationships between entities
- **Multiplicity Symbols**: Crow's Foot notation for cardinality
**Why ER Diagrams:**
- Visual clarity for complex database designs
- Communication tool for non-technical stakeholders
- Documentation of database schema
- Planning database normalization
---
## Getting Started
### Installation and Setup
Import DiagramModule in your Angular component. ER diagrams require the ER module to be injected before usage.
```typescript
import { Component, ViewEncapsulation } from '@angular/core';
import { Diagram, DiagramModule, ErDiagrams, NodeModel } from '@syncfusion/ej2-angular-diagrams';
Diagram.Inject(ErDiagrams);
@Component({
imports: [DiagramModule],
providers: [],
standalone: true,
selector: 'app-container',
template: `<ejs-diagram #diagram id="diagram" [height]="'400px'" ></ejs-diagram>`,
encapsulation: ViewEncapsulation.None,
})
export class AppComponent {}
```
### Basic ER Entity Node
Create an entity with fields:
```typescript
import { Component, ViewChild, ViewEncapsulation } from '@angular/core';
import { Diagram, DiagramModule, ErDiagrams, NodeModel, DiagramComponent } from '@syncfusion/ej2-angular-diagrams';
Diagram.Inject(ErDiagrams);
@Component({
imports: [DiagramModule],
providers: [],
standalone: true,
selector: 'app-container',
template: `<ejs-diagram #diagram id="diagram" [height]="'400px'" [nodes]="nodes"></ejs-diagram>`,
encapsulation: ViewEncapsulation.None,
})
export class AppComponent {
@ViewChild('diagram') diagram!: DiagramComponent;
nodes: NodeModel[] = [
{
id: 'Customer',
offsetX: 300,
offsetY: 200,
shape: {
type: 'Er',
header: { annotation: { content: 'Customer' } },
fields: [
{
id: 'cust_id',
name: 'CustomerID',
dataType: 'INT',
isPrimaryKey: true,
constraints: ['NotNull'],
},
{
id: 'cust_firstname',
name: 'FirstName',
dataType: 'VARCHAR(50)',
constraints: ['NotNull'],
},
{
id: 'cust_email',
name: 'Email',
dataType: 'VARCHAR(100)',
constraints: ['Unique'],
}
]
}
}
];
}
```
---
## Key Concepts
### ER Entity Nodes (ErShapeModel)
Entities represent database tables. Configure with header, fields, and styling:
```typescript
{
id: 'employee',
offsetX: 250,
offsetY: 200,
shape: {
type: 'Er',
header: {
annotation: {
content: 'EMPLOYEE',
style: { bold: true, color: 'white' }
},
height: 40,
style: { fill: '#0066cc' }
},
fields: [
{ id: 'emp_id', name: 'EmployeeID', dataType: 'int', isPrimaryKey: true },
{ id: 'emp_fname', name: 'FirstName', dataType: 'varchar', constraints: ['NotNull'] },
{ id: 'emp_lname', name: 'LastName', dataType: 'varchar', constraints: ['NotNull'] }
],
fieldDefaults: {
alternateRowColors: ['#ffffff', '#f0f0f0']
}
} as ErShapeModel
}
```
### Entity Header Configuration
The entity header displays the name of the table or entity.
```ts
header: {
annotation: {
content: 'CUSTOMER TABLE',
style: {
color: 'white',
fontSize: 13,
bold: true,
fontFamily: 'Arial'
}
},
height: 35,
style: {
fill: '#2E75B6'
}
}
```
### ER Fields (ErFieldModel)
Each field represents a column with properties:
```typescript
{
id: 'emp_id',
name: 'EmployeeID',
dataType: 'INT',
isPrimaryKey: true,
constraints: ['NotNull']
}
```
**Properties:**
- `id`: Unique field identifier within the entity
- `name`: Column display name
- `dataType`: SQL type (INT, VARCHAR(50), DECIMAL(10,2), etc.)
- `isPrimaryKey`: Indicates whether the field is the primary key.
- `isForeignKey`: Indicates whether the field is the foreign key.
- `constraints`: Array of constraints (NotNull, Unique)
### Constraints
Database constraints ensure data integrity:
```typescript
fields: [
{ id: 'emp_id', name: 'EmployeeID', dataType: 'INT', isPrimaryKey: true, constraints: ['NotNull'] },
{ id: 'emp_email', name: 'Email', dataType: 'VARCHAR(100)', constraints: ['Unique', 'NotNull'] },
{ id: 'dept_id', name: 'DepartmentID', dataType: 'INT', isForeignKey: true }
]
```
### Runtime Field Management
#### Add a Field
The `addErField` method adds a field to an ER entity node.
```ts
const entityNode = this.diagram.nodes[0];
const newField = {
id: 'customer_phone',
name: 'Phone',
dataType: 'VARCHAR(20)'
}
this.diagram.addErField(entityNode, newField)
```
##### Insert a Field at a Specific Position
To insert the field at a specific position, pass the index as the third argument:
```ts
this.diagram.addErField(entityNode, newField, 2);
```
---
#### Remove a Field
The `removeErField` method removes an existing field from an ER entity node.
```ts
// Find the field that needs to be removed from the ER entity.
const fieldToRemove = entityNode.shape.fields.find(
(field) => field.id === 'emp_email'
);
if (fieldToRemove) {
this.diagram.removeErField(entityNode, fieldToRemove);
}
```
---
#### Tracking Entity Changes
Use the `erEntityChanged` event to monitor field modifications.
```ts
public erEntityChanged(args: IErEntityChangedEventArgs): void {
// ER fields can be reordered using drag-and-drop within the entity.
if (args.cause === 'FieldsReorder' && args.state === 'Completed') {
console.log('ER fields reordered successfully.');
}
if (args.cause === 'FieldsAdd') {
console.log('Field Added');
}
if (args.cause === 'FieldsRemove') {
console.log('Field Removed');
}
}
```
The event is triggered when ER entity fields are:
- Added
- Removed
- Reordered
---
### ER Connector Properties
| Property | Description |
|----------|-------------|
| type | Defines the connector shape as 'Er' |
| relationship | Identifying or non-identifying relationship |
| sourceMultiplicity | Crow's Foot notation at source end |
| targetMultiplicity | Crow's Foot notation at target end |
## ER Relationship Type
The relationship property defines whether a relationship is:
- Identifying
- Non-identifying
```ts
shape: {
type: 'Er',
relationship: 'Identifying'
}
```
#### ER Multiplicity
Connect entities with multiplicity (how many instances relate):
```typescript
connectors: ConnectorModel[] = [
{
id: 'customer-order',
sourceID: 'employee',
targetID: 'order',
shape: {
type: 'Er',
sourceMultiplicity: { type: 'One' },
targetMultiplicity: { type: 'OneOrMany' } // One customer places many orders
}
}
]
```
**Six Multiplicity Types:**
1. **One**: Exactly one
2. **OneAndOnlyOne**: Strict one-to-one
3. **Many**: Zero, one, or many (crow's foot)
4. **ZeroOrOne**: Optional (zero or one)
5. **OneOrMany**: At least one or many
6. **ZeroOrMany**: Optional many
---
## Navigation Guide
### Getting Started
- Installation and module setup
- Basic entity creation
- CSS imports and theme configuration
- First diagram render
### Entity Configuration
- Creating ER entities with ErShapeModel
- Header properties (annotation, height, styling)
- Multiple entities in same diagram
- Entity node positioning and styling
### Field Management
- Field definition with properties (name, dataType)
- Primary key and foreign key configuration
- Constraints (Unique, NotNull)
- Alternate row colors for readability
### Runtime Operations
- Adding fields dynamically with `addErField()`
- Removing fields with `removeErField()`
- Modifying fields without recreating entity
- Field change tracking with events
### Relationships
- Creating ER connectors between entities
- Identifying vs non-identifying relationships
- Crow's Foot multiplicity symbols
- Real-world relationship examples
---
## Quick Start Example
```typescript
import { Component, ViewChild, ViewEncapsulation } from '@angular/core';
import { Diagram, DiagramComponent, DiagramModule, ErDiagrams, NodeModel, ConnectorModel } from '@syncfusion/ej2-angular-diagrams';
Diagram.Inject(ErDiagrams);
@Component({
selector: 'app-container',
template: `
<button (click)="addField()">Add Field</button>
<ejs-diagram #diagram id="diagram" width="100%" height="800px"
[nodes]="nodes" [connectors]="connectors"></ejs-diagram>
`,
standalone: true,
imports: [DiagramModule],
encapsulation: ViewEncapsulation.None
})
export class AppComponent {
@ViewChild('diagram') diagram!: DiagramComponent;
nodes: NodeModel[] = [
{
id: 'customer',
offsetX: 250,
offsetY: 200,
shape: {
type: 'Er',
header: {
annotation: { content: 'CUSTOMER', style: { bold: true, color: 'white' } },
style: { fill: '#0066cc' }
},
fields: [
{ id: 'cust_id', name: 'CustomerID', dataType: 'INT', isPrimaryKey: true },
{ id: 'cust_name', name: 'Name', dataType: 'VARCHAR(100)', constraints: ['NotNull'] },
{ id: 'cust_email', name: 'Email', dataType: 'VARCHAR(100)', constraints: ['Unique'] }
],
fieldDefaults: { alternateRowColors: ['#ffffff', '#E7F0F7'] }
}
},
{
id: 'order',
offsetX: 450,
offsetY: 500,
shape: {
type: 'Er',
header: {
annotation: { content: 'ORDER', style: { bold: true, color: 'white' } },
style: { fill: '#00aa00' }
},
fields: [
{ id: 'order_id', name: 'OrderID', dataType: 'INT', isPrimaryKey: true },
{ id: 'order_cust_id', name: 'CustomerID', dataType: 'INT', isForeignKey: true, constraints: ['NotNull'] },
{ id: 'order_date', name: 'OrderDate', dataType: 'DATETIME', constraints: ['NotNull'] }
],
fieldDefaults: { alternateRowColors: ['#ffffff', '#F0F7F0'] }
}
}
];
connectors: ConnectorModel[] = [
{
id: 'customer-order',
sourceID: 'customer',
targetID: 'order',
shape: {
type: 'Er',
relationship: 'NonIdentifying',
sourceMultiplicity: { type: 'One' },
targetMultiplicity: { type: 'OneOrMany' }
}
}
];
addField() {
const customerNode = this.diagram.nodes[0];
this.diagram.addErField(customerNode, {
id: 'cust_phone',
name: 'Phone',
dataType: 'VARCHAR(20)'
});
}
}
```
---
## Common Patterns
### Pattern 1: Master-Detail Relationship
One master entity has many detail records:
```typescript
// One Department has many Employees
sourceMultiplicity: { type: 'One' },
targetMultiplicity: { type: 'OneOrMany' }
```
### Pattern 2: Lookup Table Reference
Optional reference to lookup data:
```typescript
// One Order references zero or one Status
sourceMultiplicity: { type: 'One' },
targetMultiplicity: { type: 'ZeroOrOne' }
```
### Pattern 3: Identifying Relationship
Child entity depends on parent for identity:
```typescript
{
id: 'customer-order',
sourceID: 'customer',
targetID: 'order',
shape: {
type: 'Er',
relationship: 'Identifying',
sourceMultiplicity: { type: 'One' },
targetMultiplicity: { type: 'OneOrMany' }
}
}
```
### Pattern 4: Styled Entity Grouping
Color-code entities by type (Master, Reference, Transactional):
```typescript
style: {
fill: '#e6ffe6', // Green for master data
strokeColor: '#00cc00'
}
```
---
## Key Takeaways
✅ **ER Entities**: Use ErShapeModel to represent database tables
✅ **Fields & Constraints**: Define columns with PK, FK, Unique, NotNull
✅ **Relationships**: Connect entities with multiplicity symbols
✅ **Runtime Operations**: Add/remove fields dynamically
✅ **Styling**: Customize colors, fonts, and row colors
✅ **Events**: Track field and entity changes
---
## Related Topics
- **Diagram Basics**: Nodes, connectors, and layout
- **Shapes and Styles**: Component styling and theming
- **UML Diagrams**: Sequence and class diagrams
- **Automatic Layout**: Hierarchical layout for ER diagrams
- **Serialization**: Save and export ER diagrams
references/getting-started.md
# Getting Started
## Installation & Dependencies
The Syncfusion Angular Diagram component requires the following packages:
```bash
npm install @syncfusion/ej2-angular-core
npm install @syncfusion/ej2-angular-diagrams
```
### Package Dependencies
```
@syncfusion/ej2-angular-diagrams
├── @syncfusion/ej2-base
├── @syncfusion/ej2-data
├── @syncfusion/ej2-navigations
├── @syncfusion/ej2-inputs
├── @syncfusion/ej2-popups
├── @syncfusion/ej2-buttons
├── @syncfusion/ej2-lists
└── @syncfusion/ej2-splitbuttons
```
## Theme Setup
### Step 1: Import CSS Theme
The Diagram component needs Syncfusion® theme styles to display correctly. Syncfusion® theme packages include ready-to-use styles for supported control.
To add the styles, install the Tailwind 3 theme package using the following command:
```bash
npm install @syncfusion/ej2-tailwind3-theme
```
Add the following import to the **styles.css** file:
```css
@import "../node_modules/@syncfusion/ej2-tailwind3-theme/styles/diagram/index.css";
```
For the list of available themes, refer to the [Themes](https://ej2.syncfusion.com/angular/documentation/appearance/overview) documentation.
N> Syncfusion® provides multiple built-in themes. If the application uses a different theme, replace **@syncfusion/ej2-tailwind3-theme/styles/diagram/index.css** with the corresponding stylesheet from the desired theme package. For example, to use the Material 3 theme, import **@syncfusion/ej2-material3-theme/styles/diagram/index.css**.
## Basic Setup
### Using Standalone Components (Angular 14+)
```typescript
import { Component } from '@angular/core';
import { DiagramComponent,DiagramModule } from '@syncfusion/ej2-angular-diagrams';
@Component({
selector: 'app-diagram',
template: '<ejs-diagram #diagram></ejs-diagram>',
standalone: true,
imports: [DiagramModule],
})
export class AppComponent {}
```
### Using NgModule (Angular <14)
```typescript
import { NgModule } from '@angular/core';
import { DiagramModule } from '@syncfusion/ej2-angular-diagrams';
@NgModule({
imports: [DiagramModule],
declarations: [AppComponent]
})
export class AppModule {}
```
## Module Injection (Inject Directive)
Syncfusion uses **opt-in feature loading** via the Inject directive:
```typescript
import { Component } from '@angular/core';
import { DiagramComponent } from '@syncfusion/ej2-angular-diagrams';
import { Diagram, BpmnDiagrams, SymbolPalette, HierarchicalTree } from '@syncfusion/ej2-diagrams';
Diagram.Inject(BpmnDiagrams, HierarchicalTree);
@Component({
selector: 'app-root',
template: `<ejs-diagram #diagram id="diagram"width="100%" height="600px"></ejs-diagram>`,
standalone: true,
styleUrls: ['app.component.css'],
imports: [DiagramModule]
})
export class AppComponent {}
```
**Why Inject?**
- Small bundle (only inject what you use)
- Clear feature dependencies in code
- Prevents unused code inclusion
### Common Feature Modules
| Module | Purpose |
|--------|---------|
| `BpmnDiagrams` | BPMN shapes and notation |
| `HierarchicalTree` | Hierarchical auto-layout |
| `OrganizationalChart` | Org-chart layout |
| `MindMap` | Mindmap layout |
| `RadialTree` | Radial layout |
| `ComplexHierarchicalTree` | Complex hierarchical layout |
| `DataBinding` | Bind external data sources to diagram elements |
| `Snapping` | Enables grid snapping and alignment support |
| `PrintAndExport` | Print and export diagram (PNG, SVG, JPG) |
| `SymmetricLayout` | Symmetric/force-directed graph layout |
| `ConnectorBridging` | Renders bridge arcs when connectors overlap |
| `UndoRedo` | Enables undo and redo operations |
| `DiagramCollaboration` | Real-time diagram collaboration support |
| `LayoutAnimation` | Animates layout transitions |
| `DiagramContextMenu` | Adds right-click context menu support |
| `LineRouting` | Automatic routing of connectors |
| `AvoidLineOverlapping` | Prevents connector overlaps |
| `ConnectorEditing` | Allows interactive editing of connectors |
| `LineDistribution` | Distributes connectors evenly |
| `Ej1Serialization` | Supports EJ1 diagram data serialization |
| `FlowchartLayout` | Provides flowchart layout arrangement |
| `ImportAndExportVisio` | Import/export Microsoft Visio diagrams |
## Basic Diagram Component
### Minimal Example
```typescript
import { Component } from '@angular/core';
import {
Diagram,
DiagramComponent,
DiagramModule,
UndoRedo,
} from '@syncfusion/ej2-angular-diagrams';
Diagram.Inject(UndoRedo);
@Component({
selector: 'app-root',
template: `
<ejs-diagram #diagram
[width]="'100%'"
[height]="'600px'"
[nodes]="nodes"
[connectors]="connectors">
</ejs-diagram>
`,
standalone: true,
imports: [DiagramModule],
})
export class AppComponent {
nodes = [
{ id: 'node1', width: 100, height: 100, offsetX: 100, offsetY: 100 },
{ id: 'node2', width: 100, height: 100, offsetX: 300, offsetY: 100 },
];
connectors = [{ id: 'connector1', sourceID: 'node1', targetID: 'node2' }];
}
```
## Using the Diagram Component in Templates
Add the diagram component to your Angular templates:
```html
<ejs-diagram #diagram
id="diagram"
[width]="'100%'"
[height]="'600px'"
[nodes]="nodes"
[connectors]="connectors">
</ejs-diagram>
```
## CSS Imports Summary
```typescript
// main.ts
import '@syncfusion/ej2-base';
import '@syncfusion/ej2-angular-theme-default/styles/material.css';
```
## Verification
To verify setup is working:
```typescript
@Component({
selector: 'app-test',
template: `
<ejs-diagram #diagram
[width]="'400px'"
[height]="'300px'"
[nodes]="[{ id: 'test', offsetX: 100, offsetY: 100, width: 80, height: 80 }]">
</ejs-diagram>
`,
standalone: true,
imports: [DiagramModule],
})
export class TestComponent {}
```
If a white diagram canvas appears, setup is successful. Now proceed to [nodes.md](nodes.md) to add content.
references/groups-and-containers.md
# Groups and Containers
## Overview
**Groups** and **Containers** organize multiple nodes together, allowing them to be moved and styled as a single unit.
### Group vs Container
| Feature | Group | Container |
|---------|-------|-----------|
| **Visual** | No visible border | Has visible border/padding |
| **Children** | Multiple nodes | Multiple nodes |
| **Layout** | Manual positioning | Can control child layout |
| **Nesting** | Yes | Yes |
---
## Creating Groups
### Grouping Nodes
```typescript
// Create group by setting parentId
nodes = [
{
id: 'rectangle1',
offsetX: 100,
offsetY: 100,
width: 100,
height: 100,
},
{
id: 'rectangle2',
offsetX: 200,
offsetY: 200,
width: 100,
height: 100,
},
{
id: 'group',
children: ['rectangle1', 'rectangle2'],
},
];
```
### Programmatic Grouping
```typescript
// Select all the elements
diagram.selectAll();
// Groups the selected nodes and connectors in the diagram.
diagram.group();
```
---
## Group Operations
### Add Children to Group
```typescript
// Add child to the group node
diagram.addChildToGroup(groupNode, childNode);
```
### Remove from Group
```typescript
// Remove child from the group node
diagram.removeChildFromGroup (groupNode, childNode);
```
### Get Group Children
```typescript
const group = diagram.nodes.find(n => n.id === 'group1');
const children = group.children;
```
---
## Container Nodes
### Create Container
```typescript
{
id: 'container1',
offsetX: 250,
offsetY: 250,
width: 400,
height: 300,
shape: {
type: 'Container',
children: [] // or actual children ids
},
style: {
fill: '#F0F0F0',
strokeColor: '#999999',
strokeWidth: 2
}
}
```
### Add Children to Container
```typescript
// 1) Define child nodes first
const nodes = [
{
id: 'node1',
offsetX: 200, offsetY: 200, width: 100, height: 60
},
{
id: 'node2',
offsetX: 320, offsetY: 280, width: 100, height: 60
},
// 2) Define the container and list child IDs
{
id: 'container1',
offsetX: 260,
offsetY: 240,
width: 300,
height: 220,
shape: {
type: 'Container',
children: ['node1', 'node2']
},
padding: { left: 10, top: 10, right: 10, bottom: 10 }
}
];
```
## Styling Groups and Containers
### Group Border (Container)
```typescript
{
id: 'group1',
offsetX: 300,
offsetY: 250,
width: 420,
height: 300,
style: {
fill: 'transparent',
strokeColor: '#999999',
strokeWidth: 2,
dashArray: '5,5' // Dashed border
}
}
```
### Shadow and Effects
```typescript
{
id: 'container1',
style: {
fill: '#F0F0F0',
shadow: {
angle: 45,
blur: 10,
color: 'rgba(0,0,0,0.2)',
distance: 3
}
}
}
```
---
**→ Next: Create reusable symbols with [symbol palette](symbol-palette.md)**
references/interaction-and-tools.md
# Interaction and Tools
## Table of Contents
- [Overview](#overview)
- [Selection and Interaction](#selection-and-interaction)
- [Tool Modes](#tool-modes)
- [Constraints](#constraints)
- [Commands](#commands)
- [Undo and Redo](#undo-and-redo)
- [Context Menu](#context-menu)
- [User Handles](#user-handles)
## Overview
**Interaction** enables users to select, drag, resize, and modify diagram elements. **Tools** control which operations are allowed.
---
## Selection and Interaction
### Select Nodes
```typescript
// Single node selection
diagram.select([diagram.nodes[0]]);
// Multiple selection
diagram.selectAll();
// Deselect
diagram.clearSelection();
// Check selection
const selectedItems = diagram.selectedItems.nodes;
```
### Selection Events
```typescript
diagram.selectionChange((args) => {
console.log('New selection:', args.newItems);
console.log('Previous selection:', args.oldItems);
console.log('State:', args.state); // Added, Removed, Changed
});
```
### Drag Nodes
```typescript
diagram.positionChange((args) => {
console.log('Dragging:', args.newValue);
console.log('Previous position:', args.oldValue);
});
```
> **Note:** The `positionChange` event is triggered only for mouse-based dragging operations and does not support keyboard-based movement interactions.
### Resize Nodes
```typescript
diagram.sizeChange((args) => {
if (args.state === 'Start') {
console.log('Size Change');
}
});
diagram.sizeChange((args) => {
if (args.state === 'Progress') {
console.log('Size Change');
}
});
diagram.sizeChange((args) => {
if (args.state === 'Completed') {
console.log('Size Change');
}
});
```
---
## Tool Modes
### Pointer Tool (Default)
Select and manipulate existing elements:
```typescript
diagram.tool = DiagramTools.ZoomPan; // Default pointer tool
```
### Draw Tool
Draw connectors manually:
```typescript
diagram.tool = DiagramTools.DrawOnce; // Draw one connector, return to pointer
diagram.tool = DiagramTools.ContinuousDraw; // Draw multiple connectors
```
---
## Constraints
Constraints control what interactions are allowed on nodes and connectors.
### Node Constraints
```typescript
{
id: 'node1',
constraints: NodeConstraints.Default // All interactions allowed
}
// Disable specific interactions
{
id: 'node1',
constraints: NodeConstraints.Default & ~NodeConstraints.Drag // Can't drag
}
{
id: 'readOnly',
constraints: NodeConstraints.Default & ~NodeConstraints.Drag & ~NodeConstraints.Resize
}
```
### Constraint Options
```typescript
NodeConstraints.Default // All allowed
NodeConstraints.Drag // Can drag
NodeConstraints.Resize // Can resize
NodeConstraints.Rotate // Can rotate
NodeConstraints.Select // Can select
NodeConstraints.Delete // Can delete
NodeConstraints.InConnect // Can be connector source
NodeConstraints.OutConnect // Can be connector target
```
### Connector Constraints
```typescript
{
id: 'connector1',
constraints: ConnectorConstraints.Default & ~ConnectorConstraints.Delete
}
```
### Diagram Constraints
```typescript
diagram.constraints = DiagramConstraints.Default;
// Disable zooming
diagram.constraints = DiagramConstraints.Default & ~DiagramConstraints.Zoom;
// Disable panning
diagram.constraints = DiagramConstraints.Default & ~DiagramConstraints.Pan;
```
---
## Commands
### Built-in Commands
```typescript
// Copy/Paste
diagram.copy();
diagram.paste();
// Cut
diagram.cut();
// Delete
diagram.delete();
// Undo/Redo (see section below)
diagram.undo();
diagram.redo();
// Group/Ungroup
diagram.group();
diagram.ungroup();
// Align
diagram.align('Left');
diagram.align('Right');
diagram.align('Top');
diagram.align('Bottom');
diagram.align('Center');
diagram.align('Middle');
// Distribute
diagram.distribute('RightToLeft');
diagram.distribute('BottomToTop');
// Arrange
diagram.sendToBack();
diagram.bringToFront();
// Zoom
diagram.zoomTo(1.2); // Zoom to 120%
diagram.fitToPage();
```
### Keyboard Shortcuts
```typescript
// Default shortcuts
Ctrl+A // Select all
Ctrl+C // Copy
Ctrl+V // Paste
Ctrl+X // Cut
Delete // Delete
Ctrl+Z // Undo
Ctrl+Y // Redo
```
### Custom Commands
```typescript
// Define custom command
const customCmd = {
name: 'myCommand',
execute: () => {
console.log('Custom command executed');
},
undo: () => {
console.log('Undo custom command');
},
redo: () => {
console.log('Redo custom command');
}
};
// Execute from keyboard
diagram.keyDown((args) => {
if (args.key === 'q') {
}
});
```
---
## Undo and Redo
### Enable/Disable Undo/Redo
```typescript
diagram.historyManager.canUndo = true;
diagram.historyManager.canRedo = true;
```
### Undo/Redo Stack Size
```typescript
diagram.historyManager.stackLimit = 50; // Max undo steps
```
### Manual Undo/Redo
```typescript
diagram.undo();
diagram.redo();
diagram.historyManager.clearHistory(); // Clear all history
```
### Listen to History Changes
```typescript
diagram.historyChange((args) => {
console.log('Can undo:', args.undoCount > 0);
console.log('Can redo:', args.redoCount > 0);
});
```
---
## Context Menu
### Enable Context Menu
```typescript
diagram.contextMenu = {
show: true,
items: ['Cut', 'Copy', 'Paste', 'Delete', 'Group', 'Ungroup']
};
```
### Context Menu Items
```typescript
diagram.contextMenu = {
show: true,
items: [
'Cut',
'Copy',
'Paste',
'Delete',
{ text: 'Custom', id: 'custom', target: '.e-diagram' },
'Group',
'Ungroup',
'SelectAll',
'SendToBack',
'BringToFront'
]
};
```
### Custom Menu Item Handler
```typescript
diagram.contextMenuClick((args) => {
if (args.item.id === 'custom') {
console.log('Custom item clicked');
// Perform action
}
});
```
---
## User Handles
Custom connection handles for visual feedback in diagrams:
### Define User Handles
```typescript
{
id: 'node1',
userHandles: [
{
name: 'handle1',
position: 'TopCenter',
offset: { x: 0.5, y: 0 },
side: 'Top',
icon: {
shape: 'Circle',
width: 15,
height: 15,
fill: '#FF0000'
}
}
]
}
```
### Handle Positions
```
TopLeft, TopCenter, TopRight
MiddleLeft, MiddleCenter, MiddleRight
BottomLeft, BottomCenter, BottomRight
```
### Handle Events
```typescript
diagram.onUserHandleMouseDown((args) => {
if (args.source instanceof UserHandle) {
console.log('Handle clicked:', args.source.name);
}
});
diagram.onUserHandleMouseEnter((args) => {
if (args.element) {
args.element.pathColor = 'red';
args.element.backgroundColor = 'pink';
}
});
diagram.onUserHandleMouseUp((args) => {
if (args.element) {
args.element.pathColor = 'yellow';
args.element.backgroundColor = 'pink';
}
});
diagram.onUserHandleMouseLeave((args) => {
if (args.element) {
args.element.pathColor = 'green';
args.element.backgroundColor = 'yellow';
}
});
```
---
**→ Next: Save and export diagrams with [serialization and export](serialization-and-export.md)**
references/labels-and-annotations.md
# Labels and Annotations
## Table of Contents
- [Overview](#overview)
- [Adding Annotations](#adding-annotations)
- [Label Appearance](#label-appearance)
- [Node vs Connector Labels](#node-vs-connector-labels)
- [Label Positioning](#label-positioning)
- [Label Interaction](#label-interaction)
- [Label Events](#label-events)
- [Formatting Labels](#formatting-labels)
## Overview
**Annotations** (labels) are text elements added to nodes and connectors to provide context and information.
### Key Properties
```typescript
interface Annotation {
content: string; // Text content
offset?: number; // Position (0-1 on connector, or point)
horizontalAlignment?: 'Left' | 'Center' | 'Right';
verticalAlignment?: 'Top' | 'Center' | 'Bottom';
style?: AnnotationStyle; // Font, color, size
margin?: Margin; // Spacing
rotateAngle?: number; // Rotation in degrees
width?: number; // Width in pixels
height?: number; // Height in pixels
}
```
## Adding Annotations
### Node Annotations
```typescript
{
id: 'node1',
offsetX: 150,
offsetY: 150,
width: 100,
height: 100,
annotations: [
{
content: 'Process',
style: { fontSize: 14, color: 'black', bold: true }
}
]
}
```
### Connector Annotations
```typescript
{
id: 'connector1',
sourceID: 'node1',
targetID: 'node2',
annotations: [
{
content: 'Approves',
offset: 0.5 // Middle of connector
}
]
}
```
### Multiple Annotations
```typescript
{
id: 'node1',
annotations: [
{
content: 'Primary Label',
offset: { x: 0.5, y: 0.3 }
},
{
content: 'Secondary Label',
offset: { x: 0.5, y: 0.7 },
style: { fontSize: 10, fill: '#999999' }
}
]
}
```
---
## Label Appearance
### Font Styling
```typescript
annotations: [
{
content: 'Styled Text',
style: {
fontSize: 14, // Points
fontFamily: 'Arial', // Font name
bold: true,
italic: false,
fill: '#000000', // Text color
textDecoration: 'Underline', // None, Underline, Overline, LineThrough
textWrapping: 'Wrap' // Wrap, WrapWithOverflow, NoWrap
}
}
]
```
### Alignment
```typescript
annotations: [
{
content: 'Left Aligned',
horizontalAlignment: 'Left',
verticalAlignment: 'Top'
},
{
content: 'Center Aligned',
horizontalAlignment: 'Center',
verticalAlignment: 'Center'
}
]
```
### Background and Border
```typescript
annotations: [
{
content: 'Boxed Label',
style: {
fill: '#FFFF00', // Background color
color: '#000000'
},
margin: {
top: 10,
bottom: 10,
left: 10,
right: 10
}
}
]
```
### Rotation
```typescript
annotations: [
{
content: 'Rotated 45°',
rotateAngle: 45
}
]
```
---
## Node vs Connector Labels
### Node Labels
Labels on **nodes** are centered within the node shape:
```typescript
{
id: 'node1',
offsetX: 150,
offsetY: 150,
width: 100,
height: 100,
annotations: [
{
content: 'Process',
offset: { x: 0.5, y: 0.5 } // Center of node
}
]
}
```
### Connector Labels
Labels on **connectors** use `offset` (0-1 position along connector):
```typescript
{
id: 'connector1',
sourceID: 'node1',
targetID: 'node2',
annotations: [
{
content: 'Approves',
offset: 0.5 // Middle (0.5 = center, 0 = source, 1 = target)
}
]
}
```
### Label Offset Values
```typescript
offset: 0.0 // At connector start (source)
offset: 0.25 // 1/4 way along
offset: 0.5 // Middle (center)
offset: 0.75 // 3/4 way along
offset: 1.0 // At connector end (target)
```
---
## Label Positioning
### Relative to Node
```typescript
{
id: 'node1',
annotations: [
{
content: 'Top',
offset: { x: 0.5, y: 0 } // Top of node
},
{
content: 'Bottom',
offset: { x: 0.5, y: 1 } // Bottom of node
},
{
content: 'Left',
offset: { x: 0, y: 0.5 } // Left side
}
]
}
```
### Margin from Node
```typescript
annotations: [
{
content: 'Outward',
margin: {
top: 20,
right: 20,
bottom: 20,
left: 20
}
}
]
```
---
## Label Interaction
### Editable Labels
```typescript
// Enable editing on specific annotations
annotations: [
{
content: 'Editable',
constraints: AnnotationConstraints.Interaction
}
]
```
### Prevent Editing
```typescript
annotations: [
{
content: 'Read-only',
constraints: AnnotationConstraints.ReadOnly // Disable editing
}
]
```
### Draggable Labels
```typescript
annotations: [
{
content: 'Draggable',
constraints: AnnotationConstraints.Draggable | AnnotationConstraints.Editable
}
]
```
---
## Label Events
### Label Edit Completed
```typescript
template: `<ejs-diagram #diagram id="diagram" width="100%" height="580px" (textEdit)="textEdit($event)">
<e-nodes>
<e-node id='node1' [offsetX]=150 [offsetY]=150 [width]=100 [height]=100>
<e-node-annotations>
<e-node-annotation id="label1" content="Annotation">
</e-node-annotation>
</e-node-annotations>
</e-node>
</e-nodes>
</ejs-diagram>`
public onTextEdit(args: ITextEditEventArgs): void {
// Fires after editing is committed
// args.oldValue / args.newValue contain the label text before/after edit.
// Example:
// console.log('Old:', args.oldValue, 'New:', args.newValue);
}
```
### Label Double Click
```typescript
template: `<ejs-diagram #diagram id="diagram" width="100%" height="580px" (doubleClick)="doubleClick($event)">
<e-nodes>
<e-node id='node1' [offsetX]=150 [offsetY]=150 [width]=100 [height]=100>
<e-node-annotations>
<e-node-annotation id="label1" content="Annotation">
</e-node-annotation>
</e-node-annotations>
</e-node>
</e-nodes>
</ejs-diagram>`
public doubleClick(args: IDoubleClickEventArgs): void {
// Handle double-click event for custom logic
}
```
---
## Formatting Labels
### HTML Content
```typescript
const nodes: NodeModel[] = [
{
id: 'node1', offsetX: 150, offsetY: 150, width:100, height:100,
annotations: [{ id:"label1", template:'<div><input type="button" value="Submit"></div>' }],
}
]
```
### Multi-line Text
```typescript
annotations: [
{
content: 'Line 1\nLine 2\nLine 3',
style: { textWrapping: 'Wrap' },
width: 100,
height: 60
}
]
```
### Dynamic Content
```typescript
// Update label at runtime
diagram.connectors[0].annotations[0].content = 'New Label';
diagram.dataBind();
```
### Conditional Labels
```typescript
getConnectorDefaults = (connector: ConnectorModel): ConnectorModel => {
const isDecision = connector.targetID.includes('decision');
return {
...connector,
annotations: [
{
content: isDecision ? 'Yes / No' : 'Next',
offset: 0.5,
style: {
fontSize: isDecision ? 12 : 10
}
}
]
};
};
```
---
**→ Next: Configure [ports](ports.md) for connection points on nodes**
references/layouts.md
# Layouts
## Table of Contents
- [Overview](#overview)
- [Hierarchical Tree](#hierarchical-tree)
- [Organizational Chart](#organizational-chart)
- [Mindmap Layout](#mindmap-layout)
- [Radial Tree](#radial-tree)
- [Flowchart Layout](#flowchart-layout)
- [Complex Hierarchical Tree](#complex-hierarchical-tree)
- [Configuration and Customization](#configuration-and-customization)
- [Layout Events](#layout-events)
## Overview
**Layouts** automatically position nodes and connectors based on hierarchical or structural relationships.
### Layout Types
| Layout | Use Case |
|--------|----------|
| **HierarchicalTree** | Tree hierarchies (org-charts, family trees) |
| **OrganizationalChart** | Organizational structures with swimlanes |
| **MindMap** | Brainstorming and idea mapping |
| **RadialTree** | Nodes arranged in circular pattern |
| **Flowchart** | Sequential processes with swimlanes |
| **ComplexHierarchicalTree** | Multiple roots or complex trees |
---
## Hierarchical Tree
### Purpose
Arranges nodes in top-to-bottom, bottom-to-top, left-to-right, or right-to-left hierarchies.
### Enable Module
```typescript
import { Diagram, HierarchicalTree } from '@syncfusion/ej2-angular-diagrams';
Diagram.Inject(HierarchicalTree);
```
### Basic Configuration
```typescript
layout: {
type: 'HierarchicalTree',
orientation: 'TopToBottom', // TopToBottom, BottomToTop, LeftToRight, RightToLeft
horizontalSpacing: 50,
verticalSpacing: 50
}
```
### Orientations
```typescript
orientation: 'TopToBottom' // Root at top, children below
orientation: 'BottomToTop' // Root at bottom, children above
orientation: 'LeftToRight' // Root at left, children to right
orientation: 'RightToLeft' // Root at right, children to left
```
### Spacing Configuration
```typescript
layout: {
type: 'HierarchicalTree',
orientation: 'TopToBottom',
horizontalSpacing: 80, // Space between siblings
verticalSpacing: 60, // Space between levels
margin: {
top: 10,
left: 10,
right: 10,
bottom: 10
}
}
```
---
## Organizational Chart
### Purpose
Specialized hierarchical layout for organizational structures with swimlanes showing reporting relationships.
### Enable Module
```typescript
import { Diagram, HierarchicalTree } from '@syncfusion/ej2-angular-diagrams';
Diagram.Inject(HierarchicalTree);
```
### Configuration
```typescript
layout: {
type: 'OrganizationalChart',
horizontalSpacing: 100,
verticalSpacing: 80
}
```
### Org-Chart Example
```typescript
// Parent/child relationship via parentId
let item = [
{ id: 'ceo', parentId: null, name: 'CEO' },
{ id: 'cto', parentId: 'ceo', name: 'CTO' },
{ id: 'dev1', parentId: 'cto', name: 'Developer 1' },
{ id: 'dev2', parentId: 'cto', name: 'Developer 2' }
]
let dataSourceSettings = {
id: 'id',
parentId: 'parentId',
dataManager: new DataManager(item)
}
```
---
*Note*: For organizational charts, set `layout.type` to `OrganizationalChart`. This layout is built on top of the `HierarchicalTree` module, so you must still inject the `HierarchicalTree` module for it to work correctly.
## Mindmap Layout
### Purpose
Arranges nodes radially around a central topic with branches representing sub-topics.
### Enable Module
```typescript
import { Diagram, MindMap } from '@syncfusion/ej2-angular-diagrams';
Diagram.Inject(MindMap);
```
### Configuration
```typescript
layout: {
type: 'MindMap',
horizontalSpacing: 50,
verticalSpacing: 50
}
```
### Structure
```typescript
// Central node (root)
nodes: [
{
id: 'central',
shape: { type: 'Basic', shape: 'Ellipse' },
annotations: [{ content: 'Main Topic' }]
},
// Child nodes (first level branches)
{
id: 'branch1',
shape: { type: 'Basic', shape: 'Rectangle' }
},
// Grandchild nodes (second level branches)
{
id: 'subbranch1',
shape: { type: 'Basic', shape: 'Rectangle' }
}
]
// Connectors link parent → child relationship
connectors: [
{ id: 'conn1', sourceID: 'central', targetID: 'branch1' },
{ id: 'conn2', sourceID: 'branch1', targetID: 'subbranch1' }
]
```
---
## Radial Tree
### Purpose
Arranges nodes in concentric circles around a central node.
### Enable Module
```typescript
import { Diagram, RadialTree } from '@syncfusion/ej2-angular-diagrams';
Diagram.Inject(RadialTree);
```
### Configuration
```typescript
layout: {
type: 'RadialTree',
horizontalSpacing: 50,
verticalSpacing: 50
}
```
---
## Flowchart Layout
### Purpose
Arranges nodes left-to-right or top-to-bottom for sequential processes.
### Enable Module
```typescript
import { Diagram, FlowchartLayout} from '@syncfusion/ej2-angular-diagrams';
Diagram.Inject(FlowchartLayout);
// No separate module needed, but swimlanes recommended
```
### Configuration
```typescript
layout: {
type: 'Flowchart',
orientation: 'TopToBottom',
horizontalSpacing: 60,
verticalSpacing: 60
}
```
---
## Complex Hierarchical Tree
### Purpose
Handles multiple root nodes or non-standard hierarchies (e.g., dependency graphs).
### Enable Module
```typescript
import { Diagram, ComplexHierarchicalTree } from '@syncfusion/ej2-angular-diagrams';
Diagram.Inject(ComplexHierarchicalTree);
```
### Configuration
```typescript
layout: {
type: 'ComplexHierarchicalTree',
orientation: 'TopToBottom',
horizontalSpacing: 80,
verticalSpacing: 80
}
```
### Multiple Roots
```typescript
// Multiple nodes with no parent
nodes: [
{ id: 'root1', ... },
{ id: 'root2', ... },
{ id: 'child1', parentId: 'root1', ... },
{ id: 'child2', parentId: 'root2', ... }
]
```
---
## Configuration and Customization
### DoLayout
Trigger layout calculation:
```typescript
// Define layout
diagram.layout = {
type: 'HierarchicalTree',
orientation: 'TopToBottom',
horizontalSpacing: 50,
verticalSpacing: 50
};
// Apply to nodes
diagram.doLayout();
```
---
## Layout Events
### Layout Completed
```typescript
diagram.layoutUpdated((args) => {
if (args.state === 'Completed') {
console.log('Layout rendering completed');
}
});
```
### Node Positioning Changed
```typescript
diagram.positionChange((args) => {
if (args.state === 'Completed') {
console.log('Node repositioned:', args.element);
}
});
```
---
## Example: Org-Chart
```typescript
import { Component, ViewEncapsulation, ViewChild } from '@angular/core';
import { DiagramComponent,Diagram, HierarchicalTree,ConnectorModel,NodeModel } from '@syncfusion/ej2-angular-diagrams';
import { DataManager, Query } from '@syncfusion/ej2-data';
Diagram.Inject(HierarchicalTree);
@Component({
imports: [ DiagramModule ],
selector: 'app-org-chart',
template: `
<ejs-diagram #diagram
[layout]="layout"
[dataSourceSettings]="dataSourceSettings"
[getNodeDefaults]="getNodeDefaults"
[getConnectorDefaults]="getConnectorDefaults">
</ejs-diagram>
`,
providers: [HierarchicalTreeService]
})
export class OrgChartComponent {
layout = {
type: 'OrganizationalChart',
horizontalSpacing: 100,
verticalSpacing: 80
};
data = [
{ id: 'ceo', parentId: null, name: 'CEO' },
{ id: 'cto', parentId: 'ceo', name: 'CTO' },
{ id: 'dev1', parentId: 'cto', name: 'Developer 1' }
];
dataSourceSettings = {
id: 'id',
parentId: 'parentId',
dataManager: new DataManager(data)
};
getNodeDefaults = (node:NodeModel) => ({
node.width = 75;
node.height = 40;
node.shape: { type: 'Flow', shape: 'Process' }
return node;
});
getConnectorDefaults = (connector:ConnectorModel) => ({
connector.type = 'Orthogonal';
return connector;
});
}
```
---
**→ Next: Structure diagrams with [swimlanes](swimlanes.md)**
references/nodes.md
# Nodes
## Table of Contents
- [Overview](#overview)
- [Creating Nodes](#creating-nodes)
- [Positioning and Sizing](#positioning-and-sizing)
- [Node Shapes](#node-shapes)
- [Styling Nodes](#styling-nodes)
- [Node Customization](#node-customization)
- [Expand/Collapse](#expandcollapse)
- [Node Events](#node-events)
- [getNodeDefaults Pattern](#getnodedefaults-pattern)
## Overview
**Nodes** are the primary building blocks of a diagram. They represent entities, processes, decisions, and other visual elements.
### Key Node Properties
```typescript
interface Node {
id: string; // Unique identifier
width: number; // Width in pixels
height: number; // Height in pixels
offsetX: number; // X position of center
offsetY: number; // Y position of center
shape: { type: string; shape?: string }; // Shape definition
annotations?: Annotation[]; // Labels on node
style?: NodeStyle; // Fill, strokeColor, colors
constraints?: NodeConstraints; // Allow/disable interactions
// ...many more properties in NodeModel
}
```
## Creating Nodes
### Add Nodes to Diagram
```typescript
// Define nodes array
nodes = [
{
id: 'node1',
width: 100,
height: 100,
offsetX: 150,
offsetY: 150
},
{
id: 'node2',
width: 100,
height: 100,
offsetX: 350,
offsetY: 150
}
];
// Add to template
<ejs-diagram [nodes]="nodes"></ejs-diagram>
```
### Add Nodes Dynamically
```typescript
// Add a single node
diagram.add({
id: 'newNode',
width: 80,
height: 80,
offsetX: 500,
offsetY: 200,
shape: { type: 'Flow', shape: 'Process' }
});
// Add multiple nodes at once
const nodesArray = [node1, node2, node3];
diagram.addElements(nodesArray);
```
> **Note:** The method `addNode()` does not exist. Use `add()` for a single node and `addElements()` for multiple nodes.
### Remove Nodes
```typescript
// Remove by ID
diagram.remove('node1');
// Remove multiple
diagram.remove(['node1', 'node2']);
```
## Positioning and Sizing
### Absolute Positioning
Nodes are positioned by **center point** (offsetX, offsetY):
```typescript
{
id: 'process',
offsetX: 200, // Center X
offsetY: 150, // Center Y
width: 100, // Total width
height: 80 // Total height
}
```
**Note:** The node's top-left will be at:
- X = offsetX - (width / 2)
- Y = offsetY - (height / 2)
### Responsive Sizing
> **Note:** Percentage values for `width`, `height`, `offsetX`, and `offsetY` are **not supported**. These properties must be numbers (pixels). Responsive sizing must be handled via container resizing and manual recalculation.
### Maintaining Aspect Ratio
```typescript
{
id: 'node1',
width: 100,
height: 100,
shape: { type: 'Image', source: 'image.png' },
constraints: NodeConstraints.Default & ~NodeConstraints.Resize // Prevent resize
}
```
## Node Shapes
### Flow Chart Shapes
```typescript
shape: { type: 'Flow', shape: 'Process' }
```
**Available shapes:** Terminator, Decision, Process, Data, DirectData, SequentialData, PaperTape, Sort, MultiDocument, Collate, SummingJunction, Or, Extract, Merge, OfflineStorage, OnlineStorage, ManualInput, ManualOperation, LoopLimit, Delay
### Basic Shapes
```typescript
shape: { type: 'Basic', shape: 'Rectangle' }
// or: Ellipse, Hexagon, Pentagon, Triangle, Star, Cylinder, Parallelogram
```
### Path Shapes
```typescript
shape: {
type: 'Path',
data: 'M20 20 L20 500 L500 500 L500 20 Z' // SVG path data
}
```
### Image Shapes
```typescript
shape: {
type: 'Image',
source: 'https://example.com/avatar.png',
scale: 'Stretch' // or: Meet, Slice, None
}
```
### HTML Shapes
```typescript
shape: {
type: 'HTML',
content: '<div style="color:red;padding:5px"><b>HTML Node</b></div>'
}
```
### NSvg (Native SVG)
```typescript
shape: {
type: 'Native',
content: '<svg><circle cx="20" cy="20" r="20" fill="blue"/></svg>'
}
```
## Styling Nodes
### Basic Styling
```typescript
{
id: 'node1',
style: {
fill: '#90EE90', // Light green
strokeColor: '#228B22', // Dark green
strokeWidth: 2,
opacity: 0.8
}
}
```
### Gradients
```typescript
{
id: 'node1',
style: {
gradient: {
type: 'Linear',
x1: 0, y1: 0,
x2: 100, y2: 100,
stops: [
{ color: '#FF0000', offset: 0 },
{ color: '#0000FF', offset: 100 }
]
}
}
}
```
### Dashed Borders
```typescript
{
id: 'node1',
style: {
strokeColor: '#000000',
strokeDashArray: '5,5', // 5px dash, 5px gap
strokeWidth: 2
}
}
```
### Shadow Effect
```typescript
{
id: 'node1',
style: {
fill: '#90EE90',
shadow: {
angle: 45,
blur: 15,
color: 'rgba(0,0,0,0.3)',
distance: 5
}
}
}
```
## Node Customization
### Custom Templates
```typescript
<ejs-diagram>
<e-nodes>
<e-node
id="node1"
[offsetX]="150"
[offsetY]="150"
[template]="customTemplate">
</e-node>
</e-nodes>
</ejs-diagram>
<!-- Template -->
<ng-template #customTemplate let-data>
<div style="width:100%;height:100%;background:lightblue;border-radius:5px;">
<span>Custom Node</span>
</div>
</ng-template>
```
### Appearance with getNodeDefaults
See [getNodeDefaults Pattern](#getnodedefaults-pattern) section below.
### Tooltip on Nodes
```typescript
{
id: 'node1',
tooltip: {
content: 'This is Node 1',
position: 'TopCenter'
}
}
```
## Expand/Collapse
### Collapsible Nodes
```typescript
{
id: 'node1',
isExpanded: true, // Initially expanded
shape: { type: 'Flow', shape: 'Process' },
expandIcon: {
shape: 'Plus',
width: 20,
height: 20
},
collapseIcon: {
shape: 'Minus',
width: 20,
height: 20
}
}
```
## Node Events
### Selection Events
```typescript
diagram.selectionChange((args) => {
if (args.state === 'Changed') {
console.log('Selected nodes:', args.newItems);
console.log('Deselected nodes:', args.oldItems);
}
});
```
### Double-click and Click Events
```typescript
diagram.doubleClick((args) => {
if (args.source instanceof Node) {
console.log('Clicked node:', args.source.id);
}
});
```
### Drag Events
```typescript
diagram.dragEnter((args) => {
console.log('Dragging:', args.element);
});
diagram.drag((args) => {
console.log('New position:', args.element.offsetX, args.element.offsetY);
});
diagram.dragLeave((args) => {
console.log('Drag complete');
});
```
### Position Changed
```typescript
// Use the positionChange event for node drag/move
<ejs-diagram (positionChange)="onPositionChange($event)"></ejs-diagram>
// In your component:
onPositionChange(args: IDraggingEventArgs) {
if (args.state === 'Completed') {
console.log('Node moved to:', args.source.offsetX, args.source.offsetY);
}
}
```
### Property Change Event
```typescript
// Use the propertyChange event to track any property changes
<ejs-diagram (propertyChange)="onPropertyChange($event)"></ejs-diagram>
// In your component:
onPropertyChange(args: IPropertyChangeEventArgs) {
console.log('Property changed:', args.propertyName, 'New value:', args.newValue);
}
```
## getNodeDefaults Pattern
### Purpose
Set default styles for all nodes without repeating code:
```typescript
getNodeDefaults = (node: NodeModel): NodeModel => {
return {
shape: { type: 'Flow', shape: 'Process' },
style: {
fill: '#90EE90',
strokeColor: '#228B22',
strokeWidth: 2
},
height: 80,
width: 100
};
};
// In template
<ejs-diagram [getNodeDefaults]="getNodeDefaults"></ejs-diagram>
```
### Override Defaults Per Node
```typescript
// Global defaults
getNodeDefaults = (node: NodeModel): NodeModel => {
return {
style: { fill: '#90EE90' }
};
};
// Override in specific nodes
nodes = [
{
id: 'special',
style: { fill: '#FF0000' } // This red overrides green default
}
];
```
### Common Defaults
```typescript
getNodeDefaults = (node: NodeModel): NodeModel => {
const isDecision = node.id.includes('decision');
return {
style: {
fill: isDecision ? '#FFD700' : '#90EE90',
strokeColor: '#333333',
strokeWidth: 1
},
annotations: [{
content: node.id,
style: { fontSize: 12, color: 'black' }
}],
width: 100,
height: 80
};
};
```
---
**→ Next: Add [connectors](connectors.md) to link nodes together**
references/ports.md
# Ports
## Table of Contents
- [Overview](#overview)
- [Port Types](#port-types)
- [Port Positioning](#port-positioning)
- [Port Appearance](#port-appearance)
- [Connecting to Ports](#connecting-to-ports)
- [Port Interaction](#port-interaction)
- [getPortDefaults Pattern](#getportdefaults-pattern)
## Overview
**Ports** are connection points on node boundaries where connectors can attach.
### Key Properties
```typescript
interface Port {
id: string; // Unique identifier within node
offset: { x: number; y: number }; // Position (0-1 coordinates)
shape: 'X' | 'Circle' | 'Square' | 'Custom'; // Visual appearance
width: number; // Size in pixels
height: number;
visibility: 'Visible' | 'Hidden' | 'Hover' | 'Connect'; // When to show
style: PortStyle; // Color, fill
constraints: PortConstraints; // Interaction settings
}
```
## Port Types
### Automatic Port Creation
The Diagram component supports automatic port creation when `DiagramConstraints.AutomaticPortCreation` is enabled. Users can create ports interactively by Ctrl+dragging on a node or connector. This feature is disabled by default.
**Auto-port positions (when enabled):**
- Top, Bottom, Left, Right (middle of each side)
- TopLeft, TopRight, BottomLeft, BottomRight (corners)
To enable:
```typescript
import { DiagramConstraints } from '@syncfusion/ej2-angular-diagrams';
diagram.constraints = DiagramConstraints.Default | DiagramConstraints.AutomaticPortCreation;
```
// To disable auto-ports, set the node's `ports` property to an empty array and do not enable `AutomaticPortCreation`.
### Custom Ports
```typescript
{
id: 'node1',
width: 100,
height: 100,
ports: [
{
id: 'port1',
offset: { x: 0.5, y: 0 } // Top center
},
{
id: 'port2',
offset: { x: 0.5, y: 1 } // Bottom center
},
{
id: 'port3',
offset: { x: 0, y: 0.5 } // Left center
}
]
}
```
---
## Port Positioning
### Offset Coordinates
Ports use **normalized coordinates** (0-1):
```typescript
{
id: 'port1',
offset: { x: 0, y: 0 } // Top-left corner
}
{
id: 'port2',
offset: { x: 1, y: 1 } // Bottom-right corner
}
{
id: 'port3',
offset: { x: 0.5, y: 0.5 } // Center
}
```
### Percentage-based Positioning
```typescript
// x=0.5, y=0 = middle of top edge
// x=1, y=0.5 = middle of right edge
// x=0.5, y=1 = middle of bottom edge
// x=0, y=0.5 = middle of left edge
```
### Margin from Node Edge
```typescript
{
id: 'port1',
offset: { x: 0.5, y: 0 },
margin: {
top: 5, // 5px inside from edge
left: 0,
right: 0,
bottom: 0
}
}
```
---
## Port Appearance
### Shape and Size
```typescript
{
id: 'port1',
offset: { x: 0.5, y: 0 },
shape: 'Circle', // Circle, Square, Triangle
width: 12,
height: 12
}
```
### Styling
```typescript
{
id: 'port1',
offset: { x: 0.5, y: 0 },
style: {
fill: '#FF0000', // Red port
strokeColor: '#000000', // Black border
strokeWidth: 2,
opacity: 0.8
}
}
```
### Visibility
```typescript
{
id: 'port1',
offset: { x: 0.5, y: 0 },
visibility: 'Hover' // Only show when hovering over node
}
```
**Options:** Visible, Hidden, Hover
---
## Connecting to Ports
### Connect Connector to Specific Port
```typescript
{
id: 'connector1',
sourceID: 'node1',
sourcePortID: 'port1', // Start from specific port
targetID: 'node2',
targetPortID: 'port3' // End at specific port
}
```
### Auto-port Connection
```typescript
{
id: 'connector1',
sourceID: 'node1',
targetID: 'node2'
// Automatically connects to nearest auto-ports
}
```
### Change Connection at Runtime
```typescript
// Reconnect to different port
diagram.connectors[0].sourcePortID = 'newPort';
diagram.dataBind();
```
---
## Port Interaction
### Dragging to Port
When dragging a connector endpoint, the diagram automatically snaps the connector to the nearest visible port on the node if any ports are available. If no ports are visible or defined, the connector will attach to the node boundary. This snapping behavior ensures precise connections and is especially useful for technical diagrams where port-based connections are required.
### Port Constraints
The `constraints` property controls port interaction. Use bitwise OR (`|`) to combine flags from `PortConstraints`:
```typescript
import { PortConstraints } from '@syncfusion/ej2-angular-diagrams';
{
id: 'port1',
offset: { x: 0.5, y: 0 },
constraints: PortConstraints.Default | PortConstraints.Draw // Example: allow drawing connectors from this port
}
```
**PortConstraints options:**
- `Default`: All basic interactions enabled
- `Draw`: Allows drawing connectors from the port
- `Drag`: Allows dragging the port
- `ToolTip`: Shows tooltip on hover
- `None`: Disables all interactions
---
## Setting Port Defaults
There is no `getPortDefaults` method for ports. All port properties (such as size, style, and constraints) must be set explicitly in each port object.
Example:
```typescript
{
id: 'port1',
offset: { x: 0.5, y: 0 },
width: 12,
height: 12,
visibility: 'Hover',
style: {
fill: '#0066CC',
strokeColor: '#000000'
},
constraints: PortConstraints.Default | PortConstraints.Draw
}
```
----
**→ Next: Explore [shapes and styles](shapes-and-styles.md)**
references/serialization-and-export.md
# Serialization and Export
## Overview
**Serialization** saves diagram data to JSON. **Export** converts diagrams to images, PDF, or other formats.
---
## JSON Serialization
### Save Diagram to JSON
```typescript
// Save complete diagram
const diagramData = diagram.saveDiagram();
console.log(diagramData);
// Save to file
const blob = new Blob([diagramData], { type: 'application/json' });
const url = URL.createObjectURL(blob);
const link = document.createElement('a');
link.href = url;
link.download = 'diagram.json';
link.click();
```
### Load Diagram from JSON
```typescript
// Load from variable
diagram.loadDiagram(diagramData);
// Load from file
fileInput.addEventListener('change', (event) => {
const file = event.target.files[0];
const reader = new FileReader();
reader.onload = (e) => {
const data = JSON.parse(e.target.result);
diagram.loadDiagram(data);
};
reader.readAsText(file);
});
```
### Serialized Data Structure
```json
{
"nodes": [
{
"id": "node1",
"offsetX": 100,
"offsetY": 100,
"width": 100,
"height": 100,
"shape": { "type": "Flow", "shape": "Process" }
}
],
"connectors": [
{
"id": "connector1",
"sourceID": "node1",
"targetID": "node2"
}
]
}
```
### Selective Save
```typescript
// Save only nodes
const nodes = diagram.nodes;
// Save only connectors
const connectors = diagram.connectors;
// Save custom properties
const data = {
nodes: diagram.nodes,
connectors: diagram.connectors,
customProperty: 'value'
};
```
## Mermaid Syntax Support
The Diagram component supports importing and exporting diagrams using Mermaid syntax for flowcharts, mind maps, and UML sequence diagrams.
### Save as Mermaid Syntax
```typescript
// Export diagram to Mermaid format
const mermaidData = this.diagram.saveDiagramAsMermaid();
console.log(mermaidData);
```
### Load from Mermaid Syntax (Flowchart)
```typescript
import { Component, ViewChild, ViewEncapsulation } from '@angular/core';
import { DiagramComponent, DiagramModule } from '@syncfusion/ej2-angular-diagrams';
import { Diagram, FlowchartLayout } from '@syncfusion/ej2-diagrams';
Diagram.Inject(FlowchartLayout);
@Component({
imports: [DiagramModule],
providers: [],
standalone: true,
selector: "app-container",
template: `
<button (click)="loadMermaidFlowchart()">Load Mermaid Flowchart</button>
<ejs-diagram #diagram id="diagram" width="100%" height="600px" [layout]="layout"> </ejs-diagram>`,
encapsulation: ViewEncapsulation.None
})
export class AppComponent {
@ViewChild("diagram")
public diagram!: DiagramComponent;
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;`;
this.diagram.loadDiagramFromMermaid(mermaidFlowchartData);
}
}
```
**Supported Mermaid Diagram Types:**
- Flowcharts with Flowchart layout
- Mind maps with MindMap layout
- UML sequence diagrams
---
## Detect Unsaved Changes
The `isModified` property returns `true` whenever the diagram has unsaved changes — node/connector edits, property updates, or undo/redo actions. Use it to show save indicators or warn before discarding changes.
```typescript
// Check for unsaved changes
if (this.diagram.isModified) {
const confirmed = confirm('You have unsaved changes. Discard them?');
if (!confirmed) return;
}
```
## Image Export
### Export to PNG
```typescript
// Export entire diagram
diagram.exportDiagram({format: 'PNG', fileName: 'diagram'});
// Export selected area
diagram.exportDiagram({format: 'PNG', fileName: 'diagram', region: 'PageSettings'});
```
### Export to SVG
```typescript
diagram.exportDiagram({format: 'SVG', fileName: 'diagram'});
```
### Export to JPG
```typescript
diagram.exportDiagram({format: 'JPG', fileName: 'diagram'});
```
### Export Options
```typescript
const options = {
format: 'PNG', // PNG, SVG, JPG
fileName: 'diagram',
orientation: 'Portrait', // Portrait, Landscape
scale: 1.0,
region: 'Content', // Content, PageSettings
multiplePage: false
};
diagram.exportDiagram(options);
```
### Export with Custom Size
```typescript
diagram.pageSettings = {
width: 1920,
height: 1080,
orientation: 'Landscape'
};
diagram.exportDiagram({format: 'PNG', fileName: 'large-diagram.png'});
```
---
## Print Diagram
### Print to Printer
```typescript
diagram.print({region:'Content'});
```
### Print with Options
```typescript
diagram.printSettings = {
pageOrientation: 'Portrait',
multiplePage: false,
region: 'Content'
};
diagram.print(pageOrientation);
```
---
## Visio File Import
### Import Visio (.vsdx)
```typescript
import { Component, ViewChild } from '@angular/core';
import {
Diagram,
ImportAndExportVisio,
BpmnDiagrams,
DiagramModule,
DiagramComponent,
} from '@syncfusion/ej2-angular-diagrams';
import {
UploaderModule,
UploaderComponent,
FileInfo,
} from '@syncfusion/ej2-angular-inputs';
// Inject required modules
Diagram.Inject(ImportAndExportVisio, BpmnDiagrams);
@Component({
selector: 'app-root',
standalone: true,
imports: [DiagramModule, UploaderModule],
styleUrls: ['app.component.css'],
template: `
<ejs-uploader
#defaultupload
id="fileupload"
[asyncSettings]="asyncSettings"
[multiple]="false"
[allowedExtensions]="'.vsdx'"
(success)="onUploadSuccess($event)"
>
</ejs-uploader>
<ejs-diagram
#diagram
id="diagram"
width="100%"
height="600px"
>
</ejs-diagram>
`,
})
export class AppComponent {
@ViewChild('diagram', { static: true })
public diagram!: DiagramComponent;
@ViewChild('defaultupload', { static: true })
public uploadObject!: UploaderComponent;
public asyncSettings: object = {
saveUrl:
'https://services.syncfusion.com/angular/production/api/FileUploader/Save',
removeUrl:
'https://services.syncfusion.com/angular/production/api/FileUploader/Remove',
};
public async onUploadSuccess(args: any): Promise<void> {
if (args.operation === 'upload') {
const fileObj: FileInfo = args.file;
const rawFile: File = fileObj.rawFile as File;
if (this.diagram) {
await this.diagram.importFromVisio(rawFile);
this.diagram.width = '100%';
this.diagram.height = '700px';
}
if (this.uploadObject) {
this.uploadObject.clearAll();
}
}
}
}
```
---
## EJ1 Migration Serialization
### Migrate from EJ1 Format
```typescript
// EJ1 format
const ej1Data = {
nodes: [
{
name: 'node1',
offsetX: 100,
offsetY: 100
}
]
};
// Convert to EJ2 format
const ej2Data = {
nodes: [
{
id: ej1Data.nodes[0].name,
offsetX: ej1Data.nodes[0].offsetX,
offsetY: ej1Data.nodes[0].offsetY
}
]
};
diagram.loadDiagram(ej2Data);
```
### Key Property Changes
| EJ1 | EJ2 |
|-----|-----|
| `name` | `id` |
| N/A | `width`, `height` |
| `labels` | `annotations` |
| `fromNode` | `sourceID` |
| `toNode` | `targetID` |
---
## Custom Serialization
### Custom Save Handler
```typescript
const customSave = () => {
const data = diagram.saveDiagram();
// Add custom properties
data.customMetadata = {
version: '1.0',
author: 'John Doe',
created: new Date().toISOString()
};
return data;
};
```
### Custom Load Handler
```typescript
const customLoad = (jsonData) => {
const data = JSON.parse(jsonData);
// Read custom metadata
console.log('Version:', data.customMetadata.version);
// Load diagram
diagram.loadDiagram(data);
};
```
---
**→ Next: Configure [diagram settings](diagram-settings.md) for appearance and behavior**
references/shapes-and-styles.md
# Shapes and Styles
## Quick Reference: Shape Types
### Choose the Correct `type` Value
| Use Case | Type Value | Example |
|----------|-----------|---------|
| **Flowchart shapes** (Process, Decision, Terminator) | `'Flow'` | `{ type: 'Flow', shape: 'Process' }` |
| **BPMN shapes** (Task, Event, Gateway) | `'Bpmn'` | `{ type: 'Bpmn', shape: 'Task' }` |
| **Basic geometry** (Rectangle, Circle, Star) | `'Basic'` | `{ type: 'Basic', shape: 'Rectangle' }` |
| **UML shapes** (Class, Interface, Component) | `'UmlClassifier'` | `{ type: 'UmlClassifier', shape: 'Class' }` |
| **Custom SVG path** | `'Path'` | `{ type: 'Path', data: 'M 0 0 ...' }` |
| **Images/Avatars** | `'Image'` | `{ type: 'Image', source: 'url' }` |
| **HTML content** | `'Html'` | `{ type: 'Html', content: '<div>...</div>' }` |
| **Native SVG** | `'Native'` | `{ type: 'Native', content: '<svg>...</svg>' }` |
⚠️ **Common Mistake:** Do NOT use `'FlowShape'` - Use `'Flow'` instead!
---
## Built-in Shape Types
### Flow Shapes
Used for process flowcharts and business logic:
```typescript
shape: { type: 'Flow', shape: 'Process' }
```
**Available Flow shapes:**
- `Process` - Rectangle (default process step)
- `Decision` - Diamond (yes/no branch)
- `Terminator` - Rounded rectangle (start/end)
- `Data` - Parallelogram (data input/output)
- `DirectData` - Double-sided parallelogram
- `SequentialData` - Stacked boxes
- `PaperTape` - Wavy bottom (tape symbol)
- `Sort` - Triangle pointing down
- `MultiDocument` - Stacked documents
- `Collate` - Hourglass shape
- `SummingJunction` - Circle with cross
- `Or` - Curved bow-tie
- `Extract` - Trapezoid (extraction)
- `Merge` - Inverted trapezoid
- `OfflineStorage` - Cylinder (vertical)
- `OnlineStorage` - Cylinder (horizontal)
- `ManualInput` - Trapezoid pointing down
- `ManualOperation` - Pentagon
- `LoopLimit` - Rectangle with arc
- `Delay` - Semi-circle (delay)
### Basic Shapes
Simple geometric shapes:
```typescript
shape: { type: 'Basic', shape: 'Rectangle' }
```
**Available Basic shapes:**
- `Rectangle` - Standard box
- `Ellipse` - Circle/oval
- `Hexagon` - 6-sided polygon
- `Pentagon` - 5-sided polygon
- `Triangle` - 3-sided polygon
- `Star` - 5-pointed star
- `Cylinder` - 3D cylinder
- `Parallelogram` - Slanted box
### Path (Custom SVG) Shapes
Define custom shapes using SVG path data:
```typescript
shape: {
type: 'Path',
data: 'M 20 20 L 20 500 L 500 500 L 500 20 Z'
}
```
The `data` property accepts any valid SVG path string.
### Image Shapes
Display images as nodes:
```typescript
shape: {
type: 'Image',
source: 'https://example.com/avatar.png'
}
```
**Scale options:**
- `Stretch` - Fit to node size (may distort)
- `Meet` - Fit inside without distortion
- `Slice` - Fill with cropping
- `None` - Original size
### HTML Shapes
Render HTML content as nodes:
```typescript
shape: {
type: 'HTML',
content: '<div style="background:lightblue;padding:10px;border-radius:5px;"><b>HTML Content</b></div>'
}
```
### Native SVG
Embed raw SVG:
```typescript
shape: {
type: 'Native',
content: '<svg width="100" height="100"><circle cx="50" cy="50" r="40" fill="blue"/></svg>'
}
```
---
## Styling Nodes
### Basic Colors
```typescript
{
id: 'node1',
style: {
fill: '#90EE90', // Light green background
strokeColor: '#228B22', // Dark green border
strokeWidth: 2, // Border thickness
opacity: 0.8 // Transparency (0-1)
}
}
```
### Dashed and Dotted Borders
```typescript
{
id: 'node1',
style: {
strokeColor: '#000000',
strokeWidth: 2,
dashArray: '5,5' // 5px dash, 5px gap (dashed)
}
}
// Dotted
dashArray: '1,2' // Dotted pattern
// Solid (default)
dashArray: '' // No dashes
```
### Gradients
```typescript
{
id: 'node1',
style: {
gradient: {
type: 'Linear',
x1: 0, // Start X (% of width)
y1: 0, // Start Y (% of height)
x2: 100, // End X
y2: 100, // End Y
stops: [
{ color: '#FF0000', offset: 0 }, // Red at start
{ color: '#0000FF', offset: 100 } // Blue at end
]
}
}
}
```
**Gradient types:** Linear, Radial
### Shadow Effects
```typescript
{
id: 'node1',
style: {
fill: '#90EE90',
shadow: {
angle: 45, // Shadow direction (degrees)
blur: 15, // Blur radius
color: 'rgba(0,0,0,0.3)', // Shadow color with opacity
distance: 5 // Distance from object
}
}
}
```
### Custom CSS Classes
```typescript
{
id: 'node1',
style: {
fill: '#90EE90'
},
// Add custom CSS class
// Then style with CSS
}
/* In your stylesheet */
.my-node {
filter: drop-shadow(2px 2px 4px rgba(0,0,0,0.3));
}
```
---
## Color Reference
### Named Colors
```
Common: black, white, gray, red, green, blue, yellow, orange, purple, pink, cyan, magenta
Light: lightblue, lightgreen, lightgray, lightyellow
Dark: darkblue, darkgreen, darkred, darkgray, navy
```
### Hex Colors
```
#FF0000 (Red)
#00FF00 (Green)
#0000FF (Blue)
#FFFF00 (Yellow)
#FF00FF (Magenta)
#00FFFF (Cyan)
#808080 (Gray)
```
### RGB/RGBA
```typescript
fill: 'rgb(255, 0, 0)' // Red
fill: 'rgba(255, 0, 0, 0.5)' // Semi-transparent red
```
---
## Theme Customization
### Using CSS Variables
```typescript
// In global styles.css
:root {
--diagram-fill: #90EE90;
--diagram-strokeColor: #228B22;
}
// In component
style: {
fill: 'var(--diagram-fill)',
strokeColor: 'var(--diagram-strokeColor)'
}
```
### Theme Classes
Syncfusion applies theme classes automatically:
- `.e-light` - Light theme
- `.e-dark` - Dark theme
- `.e-highcontrast` - High contrast
```typescript
// Override in style
.e-diagram .my-node {
fill: #90EE90;
}
```
---
## Text Styling
### Font Properties
```typescript
// On nodes with annotations
annotations: [
{
content: 'Styled Text',
style: {
fontSize: 14,
fontFamily: 'Arial',
bold: true,
italic: false,
color: '#000000', // Note: "color" for text, not "fill"
fill: '#000000' // Also supports "fill"
}
}
]
```
---
**→ Next: Create special diagram types with [BPMN diagrams](bpmn-diagrams.md)**
references/swimlanes.md
# Swimlanes
## Table of Contents
- [Overview](#overview)
- [Swimlane Structure](#swimlane-structure)
- [Creating Swimlanes](#creating-swimlanes)
- [Lanes and Phases](#lanes-and-phases)
- [Swimlane Children](#swimlane-children)
- [Swimlane Palette](#swimlane-palette)
- [Swimlane Interaction](#swimlane-interaction)
## Overview
**Swimlanes** organize diagram elements into horizontal or vertical lanes, typically used in BPMN process diagrams to show different actors or departments.
### Swimlane Types
- **Header** - Lane identifier (who performs activity)
- **Lanes** - Vertical or horizontal divisions
- **Phases** - Subdivision of lanes
---
## Swimlane Structure
### Basic Swimlane
```typescript
{
id: 'swimlane1',
shape: {
type: 'SwimLane',
orientation: 'Horizontal',
lanes: [
// Lanes defined here
]
},
width: 800,
height: 200,
offsetX: 400,
offsetY: 200
}
```
### Swimlane with Header
```typescript
{
id: 'swimlane1',
shape: {
type: 'SwimLane',
orientation: 'Horizontal',
header: {
annotation: {
content: 'Manager'
},
width: 50
},
lanes: [
{
id: 'lane1',
header: {
width: 50,
annotation: { content: 'Lane 1' }
}
}
]
}
}
```
---
## Lanes and Phases
### Creating Lanes
```typescript
shape: {
type: 'SwimLane',
orientation: 'Horizontal',
lanes: [
{
id: 'lane1',
header: {
width: 50,
annotation: { content: 'Department A' }
}
},
{
id: 'lane2',
header: {
width: 50,
annotation: { content: 'Department B' }
}
}
]
}
```
### Creating Phases
Phases subdivide lanes vertically:
```typescript
shape: {
type: 'SwimLane',
phases: [
{
id: 'phase1',
header: {
annotation: { content: 'Phase 1' }
}
},
{
id: 'phase2',
header: {
annotation: { content: 'Phase 2' }
}
}
]
}
```
### Orientation
```typescript
orientation: 'Horizontal' // Lanes flow vertically (top-to-bottom)
orientation: 'Vertical' // Lanes flow horizontally (left-to-right)
```
---
## Swimlane Children
### Adding Child Nodes to Lanes
To add or remove child nodes to a lane, use the dedicated methods:
#### Add Child to Lane
```typescript
// Add a child node to a specific lane
diagram.addChildToLane('lane1', {
id: 'newTask',
shape: { type: 'Flow', shape: 'Process' },
// ...other node properties
});
```
#### Remove Child from Lane
```typescript
// Remove a child node from a specific lane
diagram.removeChildFromLane('lane1', 'newTask');
```
> **Note:** These methods ensure the child is properly managed within the lane structure. Children are automatically positioned within their parent lane.
---
## Swimlane Palette
### Symbol Palette Integration
```typescript
import { SymbolPalette } from '@syncfusion/ej2-angular-diagrams';
```
### Define Swimlane Symbols
```typescript
symbolPaletteNodes = [
{
id: 'swimlane_palette',
shape: {
type: 'SwimLane',
orientation: 'Horizontal',
header: { width: 50 },
lanes: [{ id: 'lane' }]
}
}
];
<ejs-symbolpalette
[symbolWidth]="75"
[symbolHeight]="75"
[palettes]="palettes">
</ejs-symbolpalette>
```
---
## Swimlane Interaction
### Resize Swimlanes
```typescript
// Allow resizing (default enabled)
swimlane.constraints = NodeConstraints.Default
// Prevent resizing
swimlane.constraints = NodeConstraints.Default & ~NodeConstraints.Resize
```
### Move Swimlanes
```typescript
// Allow moving (default enabled)
swimlane.constraints = NodeConstraints.Default
// Prevent moving
swimlane.constraints = NodeConstraints.Default & ~NodeConstraints.Drag
```
### Add/Remove Lanes
```typescript
// Add lane dynamically using the dedicated method
diagram.addLane('swimlane1', {
id: 'newLane',
header: { annotation: { content: 'New Lane' } }
});
// Remove lane using the dedicated method
diagram.removeLane('swimlane1', 'laneToRemove');
```
### Header Editing
```typescript
// Enable editing swimlane header
diagram.doubleClick((args) => {
if (args.source instanceof SwimLane) {
// Allow header text edit
}
});
```
---
**→ Next: Group nodes with [groups and containers](groups-and-containers.md)**
references/symbol-palette.md
# Symbol Palette
## Overview
**Symbol Palette** provides a draggable library of shapes that users can drag into the diagram.
---
## Symbol Palette Setup
### Component Template
```typescript
import { SymbolPaletteComponent } from '@syncfusion/ej2-angular-diagrams';
@Component({
selector: 'app-palette',
template: `
<ejs-symbolpalette #symbolpalette
[symbolWidth]="75"
[symbolHeight]="75"
[palettes]="palettes">
</ejs-symbolpalette>
`
})
export class PaletteComponent {
palettes = [];
}
```
---
## Defining Palette Symbols
### Basic Symbol Definition
```typescript
palettes = [
{
id: 'flowchart',
title: 'Flowchart',
symbols: [
{
id: 'process',
shape: { type: 'Flow', shape: 'Process' },
width: 60,
height: 60,
annotations: [{ content: 'Process' }]
},
{
id: 'decision',
shape: { type: 'Flow', shape: 'Decision' },
width: 60,
height: 60,
annotations: [{ content: 'Decision' }]
}
]
},
{
id: 'basic',
title: 'Basic Shapes',
symbols: [
{
id: 'rectangle',
shape: { type: 'Basic', shape: 'Rectangle' },
width: 60,
height: 60
},
{
id: 'circle',
shape: { type: 'Basic', shape: 'Ellipse' },
width: 60,
height: 60
}
]
}
];
```
### Symbol Categories
```typescript
palettes = [
{
id: 'category1',
title: 'Category 1',
expanded: true, // Initially expanded
symbols: [...]
},
{
id: 'category2',
title: 'Category 2',
expanded: false, // Initially collapsed
symbols: [...]
}
];
```
---
## Drag and Drop
### Drag Symbols to Diagram
```html
<div style="display:flex">
<!-- Palette on left -->
<ejs-symbolpalette #palette
[palettes]="palettes">
</ejs-symbolpalette>
<!-- Diagram on right -->
<ejs-diagram #diagram
[nodes]="nodes">
</ejs-diagram>
</div>
```
### Drag Event Handling
```typescript
diagram.dragEnter((args) => {
if (args.source instanceof SymbolPalette) {
console.log('Symbol dragging into diagram:', args.element);
}
});
diagram.drop((args) => {
console.log('Dropped at:', args.position);
console.log('Dropped symbol:', args.element);
});
```
---
## Palette Customization
### Symbol Appearance
```typescript
{
id: 'process',
shape: { type: 'Flow', shape: 'Process' },
width: 80,
height: 60,
style: {
fill: '#90EE90',
strokeColor: '#228B22'
},
annotations: [
{
content: 'Process',
style: { fontSize: 12 }
}
]
}
```
### Icons and Images
```typescript
{
id: 'icon',
shape: {
type: 'Image',
source: 'assets/process-icon.png'
},
width: 60,
height: 60
}
```
### Size Configuration
```typescript
<ejs-symbolpalette
[symbolWidth]="100"
[symbolHeight]="100"
[symbolMargin]="{ left: 5, right: 5, top: 5, bottom: 5 }">
</ejs-symbolpalette>
```
---
## Search Functionality
### Enable Search
```typescript
<ejs-symbolpalette
[palettes]="palettes"
[enableSearch]="true">
</ejs-symbolpalette>
```
### Search Filter
```typescript
// Filter symbols by name or content
const filterSymbols = (searchText: string) => {
this.palettes.forEach(palette => {
palette.symbols = palette.symbols.filter(s =>
s.id.includes(searchText) ||
s.annotations?.[0]?.content?.includes(searchText)
);
});
};
```
---
## Palette Events
### Selection Changed
```typescript
palette.selectionChange((args) => {
console.log('Selected symbol:', args.selectedSymbol);
});
```
### Drag Started
```typescript
diagram.dragEnter((args) => {
if (args.source instanceof Symbol) {
console.log('Symbol drag started');
}
});
```
---
## Creating Custom Symbol Libraries
### Dynamic Symbol Library
```typescript
const createCustomPalette = (customShapes: any[]) => {
return {
id: 'custom',
title: 'Custom Symbols',
symbols: customShapes.map((shape, index) => ({
id: `custom_${index}`,
shape: shape,
width: 60,
height: 60
}))
};
};
// Use it
this.palettes.push(createCustomPalette(myCustomShapes));
```
### Symbol from Data
```typescript
const dataSource = [
{ name: 'Shape1', type: 'Basic' },
{ name: 'Shape2', type: 'Flow' }
];
const symbols = dataSource.map(item => ({
id: item.name,
shape: { type: item.type, shape: 'Process' },
annotations: [{ content: item.name }]
}));
```
---
**→ Next: Bind data to diagrams with [data binding](data-binding.md)**
references/uml-diagrams.md
# UML Diagrams
## Table of Contents
- [Overview](#overview)
- [Class Diagrams](#class-diagrams)
- [Classifiers](#classifiers)
- [UML Relationships](#uml-relationships)
- [Sequence Diagrams](#sequence-diagrams)
- [Sequence Participants](#sequence-participants)
- [Sequence Messages](#sequence-messages)
- [Activation Boxes](#activation-boxes)
- [Fragments](#fragments)
## Overview
**UML (Unified Modeling Language)** provides standardized notation for system design and documentation.
Syncfusion supports:
- **Class Diagrams** - Show classes, attributes, methods, and relationships
- **Sequence Diagrams** - Show message interactions over time with participants, lifelines, and activation boxes
### When to Use UML Diagrams
- **Class Diagrams**: Model system architecture, show inheritance hierarchies, visualize relationships between classes
- **Sequence Diagrams**: Document API workflows, show interaction sequences, design system interactions
---
## Class Diagrams
Class diagrams show the structure and relationships of classes in a system.
### Basic Class Node
```typescript
{
id: 'class1',
width: 150,
height: 150,
offsetX: 200,
offsetY: 150,
shape: {
type: 'UmlClassifier', // Correct type for UML class
classifier: 'Class',
attributes: [
{
name: 'id',
type: 'int',
isSeparator: false
},
{
name: 'name',
type: 'string',
isSeparator: false
}
],
methods: [
{
name: 'getName',
parameters: [], // Correct property name is 'parameters'
type: 'string'
},
{
name: 'setName',
parameters: [{ name: 'name', type: 'string' }],
type: 'void'
}
]
}
}
```
### Class Node Structure
```typescript
shape: {
type: 'UmlClassifier',
classifier: 'Class',
attributes: [ // Class properties
{
name: 'id',
type: 'int',
visibility: 'Private' // Private, Public, Protected, Package
}
],
methods: [ // Class operations/methods
{
name: 'getName',
parameters: [], // Method parameters
type: 'string', // Return type
visibility: 'Public'
}
]
}
```
---
## Classifiers
### Class Classifier
Standard class shape:
```typescript
classifier: 'Class'
```
### Interface Classifier
Shows interface contract:
```typescript
classifier: 'Interface'
```
### Enumeration Classifier
Shows enum values:
```typescript
classifier: 'Enumeration',
enumMembers: [
{ name: 'RED' },
{ name: 'GREEN' },
{ name: 'BLUE' }
]
```
### Package Classifier
Groups related classes:
```typescript
classifier: 'Package'
```
### Component Classifier
Represents a reusable component:
```typescript
classifier: 'Component'
```
### Visibility Modifiers
```typescript
visibility: 'Public' // + (accessible everywhere)
visibility: 'Private' // - (accessible within class)
visibility: 'Protected' // # (accessible in subclasses)
visibility: 'Package' // ~ (accessible in same package)
```
### Abstract Classes
```typescript
isAbstract: true // Class name appears in italics
```
### Static Members
```typescript
attributes: [
{
name: 'count',
isStatic: true // Underlined in diagram
}
]
```
---
## UML Relationships
Relationships show how classes are connected:
### Association
Generic relationship between classes:
```typescript
{
id: 'association',
sourceID: 'class1',
targetID: 'class2',
relationship: 'Association',
sourceMultiplicity: '1', // Cardinality at source
targetMultiplicity: '*' // Cardinality at target (0..*, 1..*, 1..1, etc.)
}
```
### Generalization (Inheritance)
Subclass inherits from superclass:
```typescript
{
id: 'generalization',
sourceID: 'subclass',
targetID: 'superclass',
relationship: 'Generalization'
// Shown as unfilled triangle arrow
}
```
### Realization (Interface Implementation)
Class implements interface:
```typescript
{
id: 'realization',
sourceID: 'class',
targetID: 'interface',
relationship: 'Realization'
// Shown as dashed line with unfilled triangle
}
```
### Aggregation
"Has-a" relationship (whole-part, but part can exist independently):
```typescript
{
id: 'aggregation',
sourceID: 'wholeClass',
targetID: 'partClass',
relationship: 'Aggregation'
// Shown as hollow diamond at whole end
}
```
### Composition
Strong "has-a" relationship (part cannot exist without whole):
```typescript
{
id: 'composition',
sourceID: 'ownerClass',
targetID: 'ownedClass',
relationship: 'Composition'
// Shown as filled diamond at owner end
}
```
### Dependency
One class depends on another:
```typescript
{
id: 'dependency',
sourceID: 'class1',
targetID: 'class2',
relationship: 'Dependency'
// Shown as dashed line with arrow
}
```
---
## Sequence Diagrams
Sequence diagrams visualize how objects communicate over time, showing the sequence of messages exchanged between participants. The `UmlSequenceDiagramModel` provides comprehensive support for UML sequence diagram creation.
### Key Elements
- **Participants**: Actors or objects participating in the interaction
- **Lifelines**: Vertical dashed lines representing participant existence over time
- **Messages**: Arrows showing communication between participants
- **Activation Boxes**: Rectangles on lifelines showing active processing periods
- **Sequence Flow**: Messages flow top-to-bottom in chronological order
---
## Sequence Participants
Participants are entities in the interaction sequence, displayed as boxes at the top with lifelines extending downward.
### Participant Types and Stereotypes
| Stereotype | Description | Usage |
|---|---|---|
| **Default** | Standard object participant (rectangle) | System components, classes |
| **Actor** | Human user (stick figure) | External actors, users |
| **Boundary** | System interface (UI, API gateway) | API endpoints, UI components |
| **Control** | Coordinator or workflow controller | Control logic, managers |
| **Entity** | Persistent data or domain object | Database objects, domain models |
| **Database** | Database storage (cylinder shape) | Database systems, storage |
### Participant Properties
| Property | Type | Description |
|---|---|---|
| `id` | string | Unique identifier for the participant |
| `content` | string | Display text for the participant |
| `stereotype` | UmlSequenceParticipantStereotype | Visual style (Actor, Boundary, Control, Entity, Database, Default) |
| `showDestructionMarker` | boolean | Shows "X" marker at end of lifeline (participant is destroyed) |
| `activationBoxes` | Array | Collection of activation boxes for this participant |
### Creating Participants
```typescript
import { Component, ViewChild, ViewEncapsulation } from '@angular/core';
import { DiagramComponent, DiagramModule } from '@syncfusion/ej2-angular-diagrams';
import { UmlSequenceDiagramModel, UmlSequenceParticipantStereotype, UmlSequenceMessageType } from '@syncfusion/ej2-diagrams';
@Component({
selector: 'app-container',
imports: [DiagramModule],
providers: [],
standalone: true,
template: `
<ejs-diagram #diagram id="diagram" width="100%" height="600px"
[model]="umlSequenceModel"></ejs-diagram>
`,
encapsulation: ViewEncapsulation.None
})
export class AppComponent {
@ViewChild('diagram') diagram!: DiagramComponent;
umlSequenceModel: UmlSequenceDiagramModel = {
participants: [
{
id: 'user',
content: 'User',
stereotype: UmlSequenceParticipantStereotype.Actor
},
{
id: 'webUI',
content: 'Web UI',
stereotype: UmlSequenceParticipantStereotype.Boundary
},
{
id: 'apiServer',
content: 'API Server',
stereotype: UmlSequenceParticipantStereotype.Control
},
{
id: 'database',
content: 'Database',
stereotype: UmlSequenceParticipantStereotype.Database
},
{
id: "System", // Unique identifier for the participant
content: "System", // Label or name of the participant
// Flag to show destruction marker at the end of the lifeline
showDestructionMarker: true,
// Activation boxes for System
activationBoxes: [
{
id: "ActSystem", // Unique identifier for the activation box
startMessageID: "MSG1", // Message ID that marks the start of the activation
endMessageID: "MSG2" // Message ID that marks the end of the activation
}
]
}
],
// Define messages exchanged between participants
messages: [
{
id: "MSG1", content: "Login Request", fromParticipantID: "user", toParticipantID: "System",
type: UmlSequenceMessageType.Synchronous
},
{
id: "MSG2", content: "Login Response", fromParticipantID: "System", toParticipantID: "user",
type: UmlSequenceMessageType.Reply
}
],
};
}
```
---
## Sequence Messages
Messages represent communication between participants, displayed as arrows with different styles based on message type.
### Message Types
| Type | Arrow | When to Use |
|---|---|---|
| **Synchronous** | Solid arrow with filled head | Method calls, API requests requiring response |
| **Asynchronous** | Open arrow | Event notifications, fire-and-forget operations |
| **Reply** | Dashed arrow | Return values, acknowledgments to synchronous calls |
| **Create** | Arrow to participant | Object instantiation during execution |
| **Delete** | X marker on lifeline | Object destruction, service termination |
| **Self** | Arrow to same lifeline | Internal processing, recursive calls |
### Message Properties
| Property | Type | Description |
|---|---|---|
| `id` | string | Unique message identifier |
| `content` | string | Message label/text |
| `fromParticipantID` | string | ID of sending participant |
| `toParticipantID` | string | ID of receiving participant |
| `type` | UmlSequenceMessageType | Message type (Synchronous, Asynchronous, Reply, Create, Delete, Self) |
### Creating Messages
```typescript
umlSequenceModel: UmlSequenceDiagramModel = {
participants: [
{ id: "User", content: "User", stereotype: UmlSequenceParticipantStereotype.Actor },
{ id: "System", content: "System", showDestructionMarker: true, },
{ id: "Logger", content: "Logger", showDestructionMarker: true, },
{ id: "SessionManager", content: "SessionManager" }
],
messages: [
{
id: 'message1',
fromParticipantID: 'User',
toParticipantID: 'System',
type: UmlSequenceMessageType.Synchronous,
content: 'Login Request'
},
{
id: 'message2',
fromParticipantID: 'System',
toParticipantID: 'User',
type: UmlSequenceMessageType.Reply,
content: 'Authenticate User'
},
{
id: 'message3',
fromParticipantID: 'System',
toParticipantID: 'Logger',
type: UmlSequenceMessageType.Asynchronous,
content: 'Query User'
},
{
id: 'message4',
fromParticipantID: 'System',
toParticipantID: 'SessionManager',
type: UmlSequenceMessageType.Create,
content: 'User Data'
},
{
id: 'message5',
fromParticipantID: 'System',
toParticipantID: 'SessionManager',
type: UmlSequenceMessageType.Delete,
content: 'Login Success'
},
{
id: 'message6',
fromParticipantID: 'System',
toParticipantID: 'System',
type: UmlSequenceMessageType.Self,
content: 'Dashboard'
}
]
};
```
---
## Activation Boxes
Activation boxes represent periods when a participant is actively processing, shown as thin rectangles on the lifeline.
### Activation Box Properties
| Property | Type | Description |
|---|---|---|
| `id` | string | Unique activation box identifier |
| `startMessageID` | string | ID of message that initiates activation |
| `endMessageID` | string | ID of message that terminates activation |
### Creating Activation Boxes
```typescript
umlSequenceModel: UmlSequenceDiagramModel = {
participants: [
{
id: "System",
content: "System",
activationBoxes: [
{
id: "ActSystem",
startMessageID: "MSG1",
endMessageID: "MSG2"
}
]
}
]
};
```
### Destruction Markers
Show when a participant is terminated:
```typescript
participants: [
{
id: 'object1',
content: 'Temporary Object',
showDestructionMarker: true // Shows X at end of lifeline
}
]
```
---
## Fragments
Fragments are used to group messages based on specific control-flow conditions such as optional execution, branching logic, and repetition. They help organize complex interaction flows within a sequence diagram.
### Common Use Cases
- Optional processing that executes only when a condition is met.
- Alternative execution paths (if/else scenarios).
- Repeated operations using loops.
- Retry workflows and validation cycles.
- Nested interaction flows and complex business processes.
### Fragment Types
The `UmlSequenceFragmentType` enum supports the following fragment types:
| Type | Description | Common Usage |
|--------|-------------|-------------|
| `Optional` | Executes enclosed messages only when a condition is satisfied | Optional validations, feature flags |
| `Alternative` | Provides multiple conditional paths where only one executes | Decision trees, success/failure scenarios |
| `Loop` | Repeats enclosed interactions based on a loop condition | Retry mechanisms, iterative processing |
### Fragment Properties
Fragments are defined using `UmlSequenceFragmentModel`.
| Property | Description |
|-----------|-------------|
| `id` | Unique identifier for the fragment |
| `type` | Fragment type (`Optional`, `Alternative`, `Loop`) |
| `conditions` | Collection of conditions associated with the fragment |
### Fragment Condition Properties
Fragment conditions are defined using `UmlSequenceFragmentConditionModel`.
| Property | Description |
|-----------|-------------|
| `content` | Condition or descriptive text |
| `messageIds` | Collection of message IDs included in the condition |
| `fragmentIds` | Collection of nested fragment IDs |
#### Creating Fragments
The following example illustrates how to create fragments with different condition types:
```ts
import { Component, ViewEncapsulation, ViewChild } from '@angular/core';
import { DiagramComponent, DiagramModule } from '@syncfusion/ej2-angular-diagrams';
import { UmlSequenceDiagramModel, UmlSequenceMessageType, UmlSequenceFragmentType, SnapSettingsModel, SnapConstraints, UmlSequenceParticipantStereotype } from "@syncfusion/ej2-diagrams";
@Component({
imports: [DiagramModule],
standalone: true,
selector: 'app-container',
template: `<ejs-diagram #diagram id="diagram" width="100%" height="700px"
[model]="umlSequenceModel"></ejs-diagram>`,
encapsulation: ViewEncapsulation.None,
})
export class AppComponent {
@ViewChild('diagram', { static: false })
public diagram?: DiagramComponent;
public snapSettings: SnapSettingsModel = { constraints: SnapConstraints.None };
// Define the UML Sequence Diagram model
umlSequenceModel: UmlSequenceDiagramModel = {
// Define the space between participants
spaceBetweenParticipants: 300,
participants: [
{ id: "Customer", content: "Customer", stereotype: UmlSequenceParticipantStereotype.Actor },
{ id: "OrderSystem", content: "Order System"},
{ id: "PaymentGateway", content: "Payment Gateway" }
],
// Define the messages passed between participants
messages: [
{
id: "MSG1", content: "Place Order", fromParticipantID: "Customer", toParticipantID: "OrderSystem",
type: UmlSequenceMessageType.Synchronous
},
{
id: "MSG2", content: "Check Stock Availability", fromParticipantID: "OrderSystem", toParticipantID: "OrderSystem",
type: UmlSequenceMessageType.Synchronous
},
{
id: "MSG3", content: "Stock Available", fromParticipantID: "OrderSystem", toParticipantID: "Customer",
type: UmlSequenceMessageType.Reply
},
{
id: "MSG4", content: "Process Payment", fromParticipantID: "OrderSystem", toParticipantID: "PaymentGateway",
type: UmlSequenceMessageType.Synchronous
},
{
id: "MSG5", content: "Payment Successful", fromParticipantID: "PaymentGateway", toParticipantID: "OrderSystem",
type: UmlSequenceMessageType.Reply
},
{
id: "MSG6", content: "Order Confirmed and Shipped", fromParticipantID: "OrderSystem", toParticipantID: "Customer",
type: UmlSequenceMessageType.Reply
},
{
id: "MSG7", content: "Payment Failed", fromParticipantID: "PaymentGateway", toParticipantID: "OrderSystem",
type: UmlSequenceMessageType.Reply
},
{
id: "MSG8", content: "Retry Payment", fromParticipantID: "OrderSystem", toParticipantID: "Customer",
type: UmlSequenceMessageType.Reply
}
],
// Define fragments for conditional visual representation
fragments: [
// Child Fragment 1 (Optional)
{
id: 1,
type: UmlSequenceFragmentType.Optional,
conditions: [
{
content: "if item is in stock",
messageIds: ["MSG4"]
}
]
},
// Child Fragment 2 (Alternative)
{
id: 2,
type: UmlSequenceFragmentType.Alternative,
conditions: [
{
content: "if payment is successful",
messageIds: ["MSG5", "MSG6"]
},
{
content: "if payment fails",
messageIds: ["MSG7", "MSG8"]
}
]
},
// Parent Fragment (Loop)
{
id: 3,
type: UmlSequenceFragmentType.Loop,
conditions: [
{
content: "while attempts less than 3",
// Use IDs of child fragments for nested conditions
fragmentIds: ['1', '2'],
}
]
},
],
};
}
```
### Customization Options
#### Adjusting Participant Spacing
Adjust this value to accommodate longer message labels or improve diagram readability.
```ts
// Define the UML Sequence Diagram model with custom spacing
const model: UmlSequenceDiagramModel = {
// Increase space between participants for better readability
spaceBetweenParticipants: 300,
participants: participants, // collection of participants in the sequence diagram
messages: messages, // collection of messages exchanged between participants
fragments: fragments // collection of sequence diagram fragments (opt, alt, loop)
}
```
---
**→ Next: Organize diagrams with [automatic layouts](layouts.md)**
SKILL.md
---
name: syncfusion-angular-diagram
description: "Build and configure Syncfusion EJ2 Angular Diagram for flowcharts, org charts, process diagrams, and data-visualization. Trigger when users ask to create nodes/connectors, apply layouts, swimlane, use BPMN/UML shapes, ER diagrams, or add interactivity like drag-drop, zoom/pan, snapping, editing, and symbol palettes."
metadata:
author: "Syncfusion Inc"
version: "34.1.29"
category: "Data Visualization"
---
# Implementing Syncfusion Angular Diagrams
## When to Use This Skill
Use this skill when you need to:
- **Create visual diagrams** (flowcharts, org-charts, BPMN, UML)
- **Build interactive diagrams** with nodes and connectors
- **Apply layouts** (hierarchical, org-chart, mindmap, radial, flowchart)
- **Design BPMN or UML** diagrams for business processes
- **Add interaction** (drag, resize, selection, undo/redo)
- **Export or serialize** diagrams to JSON/images
## Important: API Verification Required
**API Verification Required**: Always verify API class names, properties, and signatures by reading reference files (`references/*.md`) BEFORE generating code examples. Do not assume or infer class names.
⚠️ Before writing ANY code, review the **Common Mistakes** section directly below to avoid known invalid APIs and properties.
## Component Overview
The **Syncfusion Angular Diagram component** provides a visual canvas for creating and editing diagrams:
### Core Concepts
1. **Nodes** - Rectangles, circles, flowchart shapes, custom shapes
2. **Connectors** - Lines linking nodes (Straight, Orthogonal, Bezier)
3. **Labels** - Text annotations on nodes and connectors
4. **Ports** - Connection points on node boundaries
5. **Swimlanes** - Horizontal/vertical lanes for process diagrams
6. **Layouts** - Automatic positioning (hierarchical, org-chart, mindmap, etc.)
7. **BPMN Shapes** - Business process diagram standardized shapes
8. **UML Diagrams** - Class and sequence diagram notation
9. **Symbol Palette** - Draggable symbol library
10. **Data Binding** - Render diagrams from JSON data
### Key Features
- **Interactive editing:** Drag, resize, rotate nodes and connectors
- **Auto-layout:** Hierarchical, org-chart, mindmap, radial, flowchart layouts
- **BPMN & UML:** Built-in shape libraries for standard diagrams
- **Serialization:** Save/load diagrams as JSON
- **Export:** PNG, SVG, PDF image export and print
- **Undo/Redo:** Full history support
- **Virtualization:** Render large diagrams efficiently
- **Accessibility:** WCAG 2.1 compliance with keyboard navigation
## Documentation and Navigation Guide
Start with **Getting Started**, then jump to the specific feature you need:
### Core Diagram Building
📄 **Read:** [references/getting-started.md](references/getting-started.md)
- Installation and dependencies
- Theme setup (material, bootstrap, fabric)
- CSS/SCSS imports
- Basic DiagramComponent usage
- Module injection pattern
- Standalone component setup
📄 **Read:** [references/nodes.md](references/nodes.md)
- Creating and adding nodes to diagram
- Offsetting and positioning (offsetX, offsetY)
- Node dimensions (width, height)
- Built-in shapes (Flow, Basic, Path)
- Custom styling (fill, stroke, opacity)
- Node templates and appearance
- Expand/collapse node groups
- Node events (click, select, drag)
- getNodeDefaults pattern for style defaults
📄 **Read:** [references/connectors.md](references/connectors.md)
- Three connector types: Straight, Orthogonal, Bezier
- Segments configuration and pathfinding
- sourceID and targetID linking nodes
- Multiple segments in connectors
- Bezier control points and orientation
- Connector customization (stroke, fill, dashing)
- Connector events and interaction
- getConnectorDefaults pattern for style defaults
### Labels, Ports & Styling
📄 **Read:** [references/labels-and-annotations.md](references/labels-and-annotations.md)
- Adding annotations to nodes and connectors
- Label appearance (font, color, bold, italic, alignment)
- Positioning labels on connectors (start, end, center)
- Node labels vs connector labels
- Label interaction (edit mode, drag, resize)
- Label events (edit, focus, blur)
- Formatting with markdown or HTML
📄 **Read:** [references/ports.md](references/ports.md)
- Port types and positioning on nodes
- Appearance customization (shape, fill, size)
- Connecting connectors to specific ports
- Connection visibility and constraints
- Port interaction and visibility
- getPortDefaults pattern
📄 **Read:** [references/shapes-and-styles.md](references/shapes-and-styles.md)
- Built-in shape types: Flow, Basic, Path, Image, HTML, Native SVG
- Flow chart shapes (process, decision, start, end, etc.)
- Style properties (fill, stroke, strokeWidth, dashArray, shadow)
- CSS class and theme customization
- Gradient and pattern fills
### Advanced Diagrams
📄 **Read:** [references/entity-relationship-diagrams.md](references/entity-relationship-diagrams.md)
- Entity Relationship Diagram design and database schema visualization
- ER entity nodes with ErShapeModel configuration
- Entity header, fields, and constraints (primary key, foreign key, unique, not null)
- Field management at runtime (add, remove, modify)
- Entity styling and alternate row colors
- ER relationships and connectors with Crow's Foot multiplicity notation
- Complete database schema examples (Customer-Order-Product)
📄 **Read:** [references/bpmn-diagrams.md](references/bpmn-diagrams.md)
- BPMN module injection and setup
- BPMN shapes overview and standard notation
- Activities (task, subprocess, expanded subprocess, loop)
- Events (start, end, intermediate catch/throw)
- Gateways (exclusive, parallel, inclusive, complex)
- Flows (sequence flow, message flow, association)
- Data objects and data sources
- Groups and text annotations
- BPMN-specific styling and labels
📄 **Read:** [references/uml-diagrams.md](references/uml-diagrams.md)
- UML class diagram shapes
- Classifier shapes (class, interface, enumeration, package, component)
- Class attributes, methods, visibility modifiers
- UML relationships (association, generalization, aggregation, composition)
- UML sequence diagrams with participants, messages, and activation boxes
- Sequence participants with 6 stereotypes (Actor, Boundary, Control, Entity, Database, Default)
- Sequence messages with 6 types (Synchronous, Asynchronous, Reply, Create, Delete, Self)
- Activation boxes and destruction markers
- Destruction markers for participant termination
- Complete login flow and interaction sequence examples
### Layout & Structure
📄 **Read:** [references/layouts.md](references/layouts.md)
- Hierarchical tree layout with top-down, bottom-up, left-right, right-left
- Organizational chart layout
- Mindmap layout (sub-trees, branches)
- Radial tree layout
- Flowchart layout with swimlanes
- Complex hierarchical tree with extended nodes
- Automatic layout configuration
- Layout customization (orientation, spacing, margin)
- Layout events and completion
📄 **Read:** [references/swimlanes.md](references/swimlanes.md)
- Swimlane structure overview (header, children, phases)
- Creating lanes and phases within swimlanes
- Swimlane children positioning and constraints
- Swimlane headers and labeling
- Swimlane palette integration
- Swimlane interaction (resize, move, add/remove children)
📄 **Read:** [references/groups-and-containers.md](references/groups-and-containers.md)
- Grouping nodes together
- Group operations (add/remove children, expand/collapse)
- Container nodes with padding and layout
- Child positioning within containers
- Container constraints
### Interaction & Tools
📄 **Read:** [references/symbol-palette.md](references/symbol-palette.md)
- SymbolPaletteComponent setup and integration
- Defining palette symbols and categories
- Drag and drop symbols to diagram
- Palette customization (icons, search, categories, size)
- Symbol palette events
- Creating custom symbol libraries
📄 **Read:** [references/data-binding.md](references/data-binding.md)
- DataManager and dataSourceSettings configuration
- Mapping data to nodes (id, parentId, nodeTemplate)
- Rendering organizational charts from JSON data
- PostgreSQL data source integration
- setNodeTemplate for data-driven node styling
- Dynamic data updates and refresh
📄 **Read:** [references/interaction-and-tools.md](references/interaction-and-tools.md)
- Node selection, drag, and resize interactions
- Tool modes (pointer, draw, pan, pan-select)
- Constraints (node constraints, connector constraints, diagram constraints)
- Commands (keyboard shortcuts, custom commands)
- Undo and Redo functionality
- Context menu (right-click menu)
- User handles (custom connection handles)
### Serialization & Export
📄 **Read:** [references/serialization-and-export.md](references/serialization-and-export.md)
- saveDiagram and loadDiagram (JSON serialization)
- Mermaid syntax support (saveDiagramAsMermaid, loadDiagramFromMermaid)
- Detect unsaved changes with isModified property
- Export to image formats (PNG, SVG, PDF)
- Print diagram functionality
- Visio file import (.vsdx)
- EJ1 API migration serialization
- Custom serialization handling
### Configuration & Settings
📄 **Read:** [references/diagram-settings.md](references/diagram-settings.md)
- Layers (add, lock, visibility, ordering)
- Virtualization (performance optimization for large diagrams)
- Grid lines (snapping, dots/lines, step size)
- Ruler (horizontal and vertical measurement rulers)
- Scroll settings (auto-scroll, pan)
- Page settings (size, orientation, margin, fit to page)
- Tooltip configuration
- Overview panel for navigation
- Localization and language support
- Accessibility (WCAG, ARIA labels, keyboard navigation)
---
## Quick Start Example
Here's a minimal diagram to get started:
```typescript
import { Component, Inject } from '@angular/core';
import { DiagramComponent, DiagramModule } from '@syncfusion/ej2-angular-diagrams';
@Component({
selector: 'app-diagram',
template: `
<ejs-diagram #diagram
width="100%"
height="600px"
[nodes]="nodes"
[connectors]="connectors">
</ejs-diagram>
`,
standalone: true,
imports: [DiagramModule]
})
export class DiagramComponent {
nodes = [
{
id: 'start',
width: 80,
height: 80,
offsetX: 150,
offsetY: 150,
shape: { type: 'Flow', shape: 'Terminator' },
annotations: [{ content: 'START' }]
},
{
id: 'process',
width: 100,
height: 80,
offsetX: 350,
offsetY: 150,
shape: { type: 'Flow', shape: 'Process' },
annotations: [{ content: 'Process' }]
},
{
id: 'end',
width: 80,
height: 80,
offsetX: 550,
offsetY: 150,
shape: { type: 'Flow', shape: 'Terminator' },
annotations: [{ content: 'END' }]
}
];
connectors = [
{ id: 'connector1', sourceID: 'start', targetID: 'process' },
{ id: 'connector2', sourceID: 'process', targetID: 'end' }
];
}
```
For detailed setup, **read [Getting Started](references/getting-started.md)**.
---
## Module Injection Pattern
Syncfusion Diagram uses **Inject directive** for feature registration:
```typescript
import { Component, Inject } from '@angular/core';
import { BpmnDiagrams, Swimlane, SymbolPalette } from '@syncfusion/ej2-angular-diagrams';
Diagram.Inject(BpmnDiagrams, SymbolPalette)
```
**Always inject the features you use.** This keeps bundle size small and makes dependencies explicit.
---
## Common Patterns
### 1. Get Node/Connector Defaults
```typescript
getNodeDefaults(node: NodeModel): NodeModel {
return {
shape: { type: 'Flow', shape: 'Process' },
style: { fill: '#90EE90', stroke: '#228B22' }
};
}
```
### 2. Bind Data to Nodes
```typescript
dataSourceSettings: {
id: 'id',
parentId: 'parentId',
dataSource: yourDataArray
}
```
### 3. Handle Selection
```typescript
diagram.selectionChange((args) => {
console.log('Selected:', args.newItems);
});
```
### 4. Export Diagram
```typescript
diagram.exportDiagram({format: 'PNG', fileName: 'diagram'});
```
### 5. Serialize Diagram
```typescript
const diagramData = diagram.saveDiagram();
localStorage.setItem('diagram', JSON.stringify(diagramData));
```
---
## Common Mistakes
### ❌ Shape Type Errors
**Mistake:**
```typescript
// ❌ WRONG - Shape type should be 'Flow', not 'FlowShape'
shape: { type: 'FlowShape', shape: 'Process' }
```
**Correct:**
```typescript
// ✅ CORRECT - Use 'Flow' for flowchart shapes
shape: { type: 'Flow', shape: 'Process' }
// ✅ CORRECT - Use 'Bpmn' for BPMN shapes
shape: { type: 'Bpmn', shape: 'Task' }
// ✅ CORRECT - Use 'Basic' for basic geometries
shape: { type: 'Basic', shape: 'Rectangle' }
```
**Reference:** [references/shapes-and-styles.md](references/shapes-and-styles.md)
### ❌ Invalid Module in Diagram.Inject()
**Wrong:**
```typescript
// ❌ Swimlane is NOT an injectable module
Diagram.Inject(BpmnDiagrams, Swimlane);
```
**Correct:**
```typescript
// ✅ Only inject verified documented modules
Diagram.Inject(BpmnDiagrams);
```
> 📄 Verify injectable modules in: `references/bpmn-diagrams.md`,
> `references/swimlanes.md`
---
### ❌ `whiteSpace` Does Not Exist in Annotation Style
**Wrong:**
```typescript
// ❌ whiteSpace is NOT a valid TextStyleModel property
annotations: [{
content: 'Multi\nLine',
style: { fontSize: 11, whiteSpace: 'pre-wrap' } // ❌
}]
```
**Correct:**
```typescript
// ✅ Use \n in content — valid style props:
// fontSize, color, bold, italic, fontFamily,
// opacity, strokeColor, strokeWidth, fill
annotations: [{
content: 'Multi\nLine',
style: { fontSize: 11, color: '#000', bold: true }
}]
```
> 📄 Verify annotation style properties in:
> `references/labels-and-annotations.md`
---
### ❌ `updateNode()` Does Not Exist on DiagramComponent
**Wrong:**
```typescript
// ❌ updateNode() does not exist in EJ2 Diagram API
this.diagram.updateNode('nodeId', { style: { fill: '#ff0000' } });
```
**Correct:**
```typescript
// ✅ getObject() → mutate property → dataBind()
const node = this.diagram.getObject('nodeId') as NodeModel;
if (node) {
node.style!.fill = '#ff0000';
this.diagram.dataBind(); // ← always required after mutation
}
```
> 📄 Verify runtime APIs in: `references/nodes.md`
### ❌ Data Binding Configuration
**Mistake:**
```typescript
// ❌ WRONG - Missing proper data source setup
nodes = this.employeeData; // Direct array assignment
```
**Correct:**
```typescript
// ✅ CORRECT - Use dataSourceSettings with proper mapping
dataSourceSettings: {
id: 'EmployeeID',
parentId: 'ReportingManagerID',
dataSource: this.employeeData
}
// Then configure setNodeTemplate for rendering:
setNodeTemplate(obj: NodeModel): void {
// obj contains the data row
obj.width = 100;
obj.height = 60;
obj.annotations = [{
content: obj.data.EmployeeName
}];
}
```
**Reference:** [references/data-binding.md](references/data-binding.md)
### ❌ Standalone Component Setup
**Mistake:**
```typescript
// ❌ WRONG - Using traditional decorator without standalone
@Component({
selector: 'app-diagram',
templateUrl: './component.html',
imports: [DiagramModule]
// Missing: standalone: true
})
```
**Correct:**
```typescript
// ✅ CORRECT - Mark as standalone component
@Component({
selector: 'app-diagram',
templateUrl: './component.html',
standalone: true, // ← Add this
imports: [CommonModule, DiagramModule],
})
```
**Reference:** [references/getting-started.md](references/getting-started.md)
### ⚠️ Performance Tips
- **Virtualization:** For diagrams with 1000+ nodes, enable virtualization in [references/diagram-settings.md](references/diagram-settings.md)
- **Layout Performance:** Pre-compute positions for complex hierarchies
- **Connector Updates:** Use `connectorChanges` instead of recreating connectors
- **Export Large Diagrams:** Use incremental approach for PDFs > 50MB
---
## Next Steps
- Choose your diagram type: flowchart, BPMN, UML, org-chart, etc.
- Read the relevant reference files above
- Check [Syncfusion Diagram Docs](https://ej2.syncfusion.com/angular/documentation/diagram/diagram-overview/) for API details
- Explore examples in [their sample repository](https://github.com/syncfusion/ej2-angular-samples)