
Syncfusion Angular Diagram
- 213 installs
- Updated August 4, 2026
- syncfusion/angular-ui-components-skills
Use syncfusion-angular-diagram for development tasks
About
syncfusion-angular-diagram: A skill for development. This provides functionality for development workflows.
- syncfusion-angular-diagram
Syncfusion Angular Diagram by the numbers
- 213 all-time installs (skills.sh)
- +11 installs in the week ending Aug 5, 2026 (Skillselion tracking)
- Ranked #1,894 of 4,347 Backend & APIs skills by installs in the Skillselion catalog
- Data as of Aug 5, 2026 (Skillselion catalog sync)
npx skills add https://github.com/syncfusion/angular-ui-components-skills --skill syncfusion-angular-diagramAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 213 |
|---|---|
| Last updated | August 4, 2026 |
| Repository | syncfusion/angular-ui-components-skills ↗ |
What it does
Use syncfusion-angular-diagram for development tasks
Files
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
- Installation and dependencies
- Theme setup (material, bootstrap, fabric)
- CSS/SCSS imports
- Basic DiagramComponent usage
- Module injection pattern
- Standalone component setup
📄 Read: 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
- 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
- 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
- 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
- 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/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
- 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
- Lifelines, actors, messages (sync, async, return)
- Interaction frames and combined fragments
Layout & Structure
📄 Read: 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
- 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
- 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
- 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
- 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
- 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
- saveDiagram and loadDiagram (JSON serialization)
- 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
- 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:
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:
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
getNodeDefaults(node: NodeModel): NodeModel {
return {
shape: { type: 'Flow', shape: 'Process' },
style: { fill: '#90EE90', stroke: '#228B22' }
};
}2. Bind Data to Nodes
dataSourceSettings: {
id: 'id',
parentId: 'parentId',
dataSource: yourDataArray
}3. Handle Selection
diagram.selectionChange((args) => {
console.log('Selected:', args.newItems);
});4. Export Diagram
diagram.exportDiagram({format: 'PNG', fileName: 'diagram'});5. Serialize Diagram
const diagramData = diagram.saveDiagram();
localStorage.setItem('diagram', JSON.stringify(diagramData));---
Common Mistakes
❌ Shape Type Errors
Mistake:
// ❌ WRONG - Shape type should be 'Flow', not 'FlowShape'
shape: { type: 'FlowShape', shape: 'Process' }Correct:
// ✅ 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
❌ Invalid Module in Diagram.Inject()
Wrong:
// ❌ Swimlane is NOT an injectable module
Diagram.Inject(BpmnDiagrams, Swimlane);Correct:
// ✅ 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:
// ❌ whiteSpace is NOT a valid TextStyleModel property
annotations: [{
content: 'Multi\nLine',
style: { fontSize: 11, whiteSpace: 'pre-wrap' } // ❌
}]Correct:
// ✅ 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:
// ❌ updateNode() does not exist in EJ2 Diagram API
this.diagram.updateNode('nodeId', { style: { fill: '#ff0000' } });Correct:
// ✅ 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:
// ❌ WRONG - Missing proper data source setup
nodes = this.employeeData; // Direct array assignmentCorrect:
// ✅ 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
❌ Standalone Component Setup
Mistake:
// ❌ WRONG - Using traditional decorator without standalone
@Component({
selector: 'app-diagram',
templateUrl: './component.html',
imports: [DiagramModule]
// Missing: standalone: true
})Correct:
// ✅ CORRECT - Mark as standalone component
@Component({
selector: 'app-diagram',
templateUrl: './component.html',
standalone: true, // ← Add this
imports: [CommonModule, DiagramModule],
})Reference: references/getting-started.md
⚠️ Performance Tips
- Virtualization: For diagrams with 1000+ nodes, enable virtualization in references/diagram-settings.md
- Layout Performance: Pre-compute positions for complex hierarchies
- Connector Updates: Use
connectorChangesinstead 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 for API details
- Explore examples in their sample repository
BPMN Diagrams
Table of Contents
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
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
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
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
{
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):
{
id: 'subprocess',
width: 100,
height: 80,
shape: { type: 'Bpmn', shape: 'Activity', activity: { activity: 'SubProcess' } }
}Expanded Subprocess
Subprocess showing inner activities:
{
id: 'expandedSubprocess',
width: 300,
height: 200,
shape: { type: 'Bpmn', shape: 'Activity', activity: { activity: 'SubProcess', collapsed: false } }
// Contains child nodes inside
}Activity Types
// 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 instancesActivity Markers
{
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
{
id: 'start',
width: 50,
height: 50,
shape: { type: 'Bpmn', shape: 'Event', event: { event: 'Start' } }
}End Events
{
id: 'end',
width: 50,
height: 50,
shape: { type: 'Bpmn', shape: 'Event', event: { event: 'End' } }
}Intermediate Events
Occur during the process:
{
id: 'intermediate',
width: 50,
height: 50,
shape: { type: 'Bpmn', shape: 'Event', event: { event: 'Intermediate' } }
}Event Types
Within each event, specify the trigger:
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
event: {
trigger: 'Message',
event: 'Intermediate',
type: 'Catching' // Catching (default) or Throwing
}---
Gateways
Gateways control flow routing:
Exclusive Gateway (XOR)
Only one path is taken:
{
id: 'decision',
width: 50,
height: 50,
shape: { type: 'Bpmn', shape: 'Gateway', gateway: { type: 'Exclusive' } }
}Parallel Gateway
All paths are executed:
{
id: 'parallel',
width: 50,
height: 50,
shape: { type: 'Bpmn', shape: 'Gateway', gateway: { type: 'Parallel' } }
}Inclusive Gateway
One or more paths:
{
id: 'inclusive',
width: 50,
height: 50,
shape: { type: 'Bpmn', shape: 'Gateway', gateway: { type: 'Inclusive' } }
}Complex Gateway
Complex branching logic:
{
id: 'complex',
width: 50,
height: 50,
shape: { type: 'Bpmn', shape: 'Gateway', gateway: { type: 'Complex' } }
}Event-based Gateway
Routes based on events:
{
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:
{
id: 'flow1',
sourceID: 'task1',
targetID: 'task2',
shape: {
type: 'Bpmn',
flow: 'Sequence'
}
}Message Flow
Communication between pools/lanes:
{
id: 'messageFlow',
sourceID: 'task1',
targetID: 'task2',
shape: {
type: 'Bpmn',
flow: 'Message'
}
}Association
Links annotations/gateways to activities:
{
id: 'association',
sourceID: 'annotation',
targetID: 'task1',
shape: {
type: 'Bpmn',
flow: 'Association'
}
}---
Data Elements
Data Object
Represents data used in process:
{
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:
{
id: 'dataRef',
width: 50,
height: 70,
shape: {
type: 'Bpmn',
shape: 'DataObject',
dataObject: {
collection: true,
type: 'Input' // or 'Output'
}
}
}Data Source
External data source:
{
id: 'datasource',
width: 50,
height: 70,
shape: {
type: 'Bpmn',
shape: 'DataSource'
}
}---
Groups and Annotations
Group
Logical grouping of elements:
{
id: 'group',
width: 400,
height: 300,
shape: {
type: 'Bpmn',
shape: 'Group'
}
// Contains child nodes inside
}Text Annotation
Adds documentation:
{
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:
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:
// 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:
// 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
Connectors
Table of Contents
- Overview
- Connector Types
- Creating Connectors
- Segments Configuration
- Source and Target
- Multiple Segments
- Bezier Control Points
- Connector Customization
- Connector Events
- 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:
{
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):
{
id: 'connector1',
sourceID: 'node1',
targetID: 'node2',
type: 'Orthogonal'
}Use cases:
- Flowcharts (avoids line confusion)
- Circuit diagrams
- Structured layouts
3. Bezier Connector
Smooth curved lines:
{
id: 'connector1',
sourceID: 'node1',
targetID: 'node2',
type: 'Bezier'
}Use cases:
- Organic diagrams
- Mind maps
- Artistic or visual workflows
---
Creating Connectors
Add Connectors to Diagram
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
diagram.add({
id: 'newConnector',
sourceID: 'node1',
targetID: 'node2',
type: 'Orthogonal'
});
// Add multiple
const connectorArray = [conn1, conn2, conn3];
diagram.addElements(connectorArray);Remove Connectors
diagram.remove(diagram.connectors[0]);---
Segments Configuration
What Are Segments?
Segments define how the connector path is drawn:
{
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
{
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:
{
id: 'connector1',
sourceID: 'node1',
targetID: 'node2',
type: 'Orthogonal'
// No segments specified - auto-routed
}---
Source and Target
Link by Node ID
{
id: 'connector1',
sourceID: 'node1', // Start from node1
targetID: 'node2' // End at node2
}Link to Specific Ports
{
id: 'connector1',
sourceID: 'node1',
sourcePortID: 'port1', // Start port
targetID: 'node2',
targetPortID: 'port2' // End port
}See ports.md for detailed port configuration.
Dynamic Source/Target
// Change connection at runtime
diagram.connectors[0].sourceID = 'newNode1';
diagram.connectors[0].targetID = 'newNode2';
diagram.dataBind();---
Multiple Segments
Chain Multiple Paths
{
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
{
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:
{
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
{
id: 'connector1',
sourceID: 'node1',
targetID: 'node2',
style: {
strokeColor: '#FF0000', // Red line
strokeWidth: 2,
strokeDashArray: '5,5' // Dashed line
}
}Arrow Heads (Decorators)
{
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
{
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 for details.
---
Connector Events
Selection Events
diagram.selectionChange((args) => {
if (args.state === 'Changed') {
console.log('selectionChange');
//Customize
}
});Connection Changed
diagram.connectionChange((args) => {
if (args.state === 'Changed') {
console.log('connectionChange');
//Customize
}
});---
getConnectorDefaults Pattern
Purpose
Set default styles for all connectors:
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
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
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
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
dataSourceSettings = {
id: 'id', // Node ID field
parentId: 'parentId', // Parent ID field
dataSource: new DataManager(employeeData),
}Custom Mapping
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:
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
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
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
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
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:
// ❌ 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:
// ❌ 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:
// ❌ 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:
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)
Diagram Settings
Table of Contents
- Overview
- Layers
- Virtualization
- Grid Lines
- Ruler
- Scroll Settings
- Page Settings
- Tooltip
- Overview Panel
- Localization
- 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
// 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
// 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
// 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
diagram.constraints = DiagramConstraints.Default | DiagramConstraints.Virtualization;Virtualization Settings
// 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
diagram.snapSettings = {
constraints: SnapConstraints.ShowLines,
horizontalGridlines: { snapIntervals: [5] },
verticalGridlines: { snapIntervals: [5] }
};Grid Line Appearance
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
// 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
diagram.rulerSettings = {
showRulers: true,
horizontalRuler: {
thickness: 30
},
verticalRuler: {
thickness: 30
}
};Ruler Configuration
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
diagram.scrollSettings = {
canAutoScroll: true,
autoScrollBorder: { left: 15, right: 15, top: 15, bottom: 15 }
};Scroll Limits and Zoom
diagram.scrollSettings = {
minZoom: 0.2,
maxZoom: 5.0,
currentZoom: 1.0
};Scroll Position
// 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
diagram.pageSettings = {
width: 816, // 8.5" × 11" (Letter)
height: 1056,
orientation: 'Portrait',
margin: {
left: 20,
top: 20,
right: 20,
bottom: 20
}
};Standard Sizes
// 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: 1344Orientation
orientation: 'Portrait' // Taller than wide
orientation: 'Landscape' // Wider than tallFit to Page
diagram.fitToPage(); // Scale diagram to fit page---
Tooltip
Show tooltips on hover.
Enable Tooltips
diagram.tooltip = {
content: 'Diagram tooltip',
position: 'TopCenter',
relativeMode: 'Object'
};Custom Tooltip for Node
{
id: 'node1',
tooltip: {
content: 'This is Node 1',
position: 'TopCenter',
relativeMode: 'Object'
},
constraints: NodeConstraints.Default | NodeConstraints.Tooltip
}Show/Hide Tooltip Programmatically
// Show tooltip
diagram.showTooltip(diagram.nodes[0]);
// Hide tooltip
diagram.hideTooltip(diagram.nodes[0]);---
Overview Panel
Minimap showing full diagram overview.
Enable Overview
<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
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
{
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
// Apply high contrast theme
import '@syncfusion/ej2-angular-theme-default/styles/highcontrast.css';Color Accessibility
// 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.
Getting Started
Installation & Dependencies
The Syncfusion Angular Diagram component requires the following packages:
npm install @syncfusion/ej2-angular-core
npm install @syncfusion/ej2-angular-diagramsPackage 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-splitbuttonsTheme Setup
Step 1: Import CSS Theme
In your styles.css:
@import '@syncfusion/ej2-base/styles/tailwind3.css';
@import '@syncfusion/ej2-popups/styles/tailwind3.css';
@import '@syncfusion/ej2-navigations/styles/tailwind3.css';
@import '@syncfusion/ej2-angular-diagrams/styles/tailwind3.css';
Available themes:
material.css- Material Design (recommended)bootstrap5.css- Bootstrap 5 themefabric.css- Microsoft Fabric themetailwind3.css- Tailwind 3 CSS themehighcontrast.css- High contrast for accessibility
Basic Setup
Using Standalone Components (Angular 14+)
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)
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:
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
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:
<ejs-diagram #diagram
id="diagram"
[width]="'100%'"
[height]="'600px'"
[nodes]="nodes"
[connectors]="connectors">
</ejs-diagram>CSS Imports Summary
// main.ts
import '@syncfusion/ej2-base';
import '@syncfusion/ej2-angular-theme-default/styles/material.css';Verification
To verify setup is working:
@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 to add content.
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
// 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
// Select all the elements
diagram.selectAll();
// Groups the selected nodes and connectors in the diagram.
diagram.group();---
Group Operations
Add Children to Group
// Add child to the group node
diagram.addChildToGroup(groupNode, childNode);Remove from Group
// Remove child from the group node
diagram.removeChildFromGroup (groupNode, childNode);Get Group Children
const group = diagram.nodes.find(n => n.id === 'group1');
const children = group.children;---
Container Nodes
Create Container
{
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
// 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)
{
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
{
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)
Interaction and Tools
Table of Contents
Overview
Interaction enables users to select, drag, resize, and modify diagram elements. Tools control which operations are allowed.
---
Selection and Interaction
Select Nodes
// Single node selection
diagram.select([diagram.nodes[0]]);
// Multiple selection
diagram.selectAll();
// Deselect
diagram.clearSelection();
// Check selection
const selectedItems = diagram.selectedItems.nodes;Selection Events
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
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
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:
diagram.tool = DiagramTools.ZoomPan; // Default pointer toolDraw Tool
Draw connectors manually:
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
{
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
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 targetConnector Constraints
{
id: 'connector1',
constraints: ConnectorConstraints.Default & ~ConnectorConstraints.Delete
}Diagram Constraints
diagram.constraints = DiagramConstraints.Default;
// Disable zooming
diagram.constraints = DiagramConstraints.Default & ~DiagramConstraints.Zoom;
// Disable panning
diagram.constraints = DiagramConstraints.Default & ~DiagramConstraints.Pan;---
Commands
Built-in Commands
// 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
// Default shortcuts
Ctrl+A // Select all
Ctrl+C // Copy
Ctrl+V // Paste
Ctrl+X // Cut
Delete // Delete
Ctrl+Z // Undo
Ctrl+Y // RedoCustom Commands
// 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
diagram.historyManager.canUndo = true;
diagram.historyManager.canRedo = true;Undo/Redo Stack Size
diagram.historyManager.stackLimit = 50; // Max undo stepsManual Undo/Redo
diagram.undo();
diagram.redo();
diagram.historyManager.clearHistory(); // Clear all historyListen to History Changes
diagram.historyChange((args) => {
console.log('Can undo:', args.undoCount > 0);
console.log('Can redo:', args.redoCount > 0);
});---
Context Menu
Enable Context Menu
diagram.contextMenu = {
show: true,
items: ['Cut', 'Copy', 'Paste', 'Delete', 'Group', 'Ungroup']
};Context Menu Items
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
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
{
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, BottomRightHandle Events
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)
Labels and Annotations
Table of Contents
- Overview
- Adding Annotations
- Label Appearance
- Node vs Connector Labels
- Label Positioning
- Label Interaction
- Label Events
- Formatting Labels
Overview
Annotations (labels) are text elements added to nodes and connectors to provide context and information.
Key Properties
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
{
id: 'node1',
offsetX: 150,
offsetY: 150,
width: 100,
height: 100,
annotations: [
{
content: 'Process',
style: { fontSize: 14, color: 'black', bold: true }
}
]
}Connector Annotations
{
id: 'connector1',
sourceID: 'node1',
targetID: 'node2',
annotations: [
{
content: 'Approves',
offset: 0.5 // Middle of connector
}
]
}Multiple Annotations
{
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
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
annotations: [
{
content: 'Left Aligned',
horizontalAlignment: 'Left',
verticalAlignment: 'Top'
},
{
content: 'Center Aligned',
horizontalAlignment: 'Center',
verticalAlignment: 'Center'
}
]Background and Border
annotations: [
{
content: 'Boxed Label',
style: {
fill: '#FFFF00', // Background color
color: '#000000'
},
margin: {
top: 10,
bottom: 10,
left: 10,
right: 10
}
}
]Rotation
annotations: [
{
content: 'Rotated 45°',
rotateAngle: 45
}
]---
Node vs Connector Labels
Node Labels
Labels on nodes are centered within the node shape:
{
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):
{
id: 'connector1',
sourceID: 'node1',
targetID: 'node2',
annotations: [
{
content: 'Approves',
offset: 0.5 // Middle (0.5 = center, 0 = source, 1 = target)
}
]
}Label Offset Values
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
{
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
annotations: [
{
content: 'Outward',
margin: {
top: 20,
right: 20,
bottom: 20,
left: 20
}
}
]---
Label Interaction
Editable Labels
// Enable editing on specific annotations
annotations: [
{
content: 'Editable',
constraints: AnnotationConstraints.Interaction
}
]Prevent Editing
annotations: [
{
content: 'Read-only',
constraints: AnnotationConstraints.ReadOnly // Disable editing
}
]Draggable Labels
annotations: [
{
content: 'Draggable',
constraints: AnnotationConstraints.Draggable | AnnotationConstraints.Editable
}
]---
Label Events
Label Edit Completed
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
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
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
annotations: [
{
content: 'Line 1\nLine 2\nLine 3',
style: { textWrapping: 'Wrap' },
width: 100,
height: 60
}
]Dynamic Content
// Update label at runtime
diagram.connectors[0].annotations[0].content = 'New Label';
diagram.dataBind();Conditional Labels
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
Layouts
Table of Contents
- Overview
- Hierarchical Tree
- Organizational Chart
- Mindmap Layout
- Radial Tree
- Flowchart Layout
- Complex Hierarchical Tree
- Configuration and Customization
- 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
import { Diagram, HierarchicalTree } from '@syncfusion/ej2-angular-diagrams';
Diagram.Inject(HierarchicalTree);Basic Configuration
layout: {
type: 'HierarchicalTree',
orientation: 'TopToBottom', // TopToBottom, BottomToTop, LeftToRight, RightToLeft
horizontalSpacing: 50,
verticalSpacing: 50
}Orientations
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 leftSpacing Configuration
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
import { Diagram, HierarchicalTree } from '@syncfusion/ej2-angular-diagrams';
Diagram.Inject(HierarchicalTree);Configuration
layout: {
type: 'OrganizationalChart',
horizontalSpacing: 100,
verticalSpacing: 80
}Org-Chart Example
// 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
import { Diagram, MindMap } from '@syncfusion/ej2-angular-diagrams';
Diagram.Inject(MindMap);Configuration
layout: {
type: 'MindMap',
horizontalSpacing: 50,
verticalSpacing: 50
}Structure
// 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
import { Diagram, RadialTree } from '@syncfusion/ej2-angular-diagrams';
Diagram.Inject(RadialTree);Configuration
layout: {
type: 'RadialTree',
horizontalSpacing: 50,
verticalSpacing: 50
}---
Flowchart Layout
Purpose
Arranges nodes left-to-right or top-to-bottom for sequential processes.
Enable Module
import { Diagram, FlowchartLayout} from '@syncfusion/ej2-angular-diagrams';
Diagram.Inject(FlowchartLayout);
// No separate module needed, but swimlanes recommendedConfiguration
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
import { Diagram, ComplexHierarchicalTree } from '@syncfusion/ej2-angular-diagrams';
Diagram.Inject(ComplexHierarchicalTree);Configuration
layout: {
type: 'ComplexHierarchicalTree',
orientation: 'TopToBottom',
horizontalSpacing: 80,
verticalSpacing: 80
}Multiple Roots
// 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:
// Define layout
diagram.layout = {
type: 'HierarchicalTree',
orientation: 'TopToBottom',
horizontalSpacing: 50,
verticalSpacing: 50
};
// Apply to nodes
diagram.doLayout();---
Layout Events
Layout Completed
diagram.layoutUpdated((args) => {
if (args.state === 'Completed') {
console.log('Layout rendering completed');
}
});Node Positioning Changed
diagram.positionChange((args) => {
if (args.state === 'Completed') {
console.log('Node repositioned:', args.element);
}
});---
Example: Org-Chart
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)
Nodes
Table of Contents
- Overview
- Creating Nodes
- Positioning and Sizing
- Node Shapes
- Styling Nodes
- Node Customization
- Expand/Collapse
- Node Events
- getNodeDefaults Pattern
Overview
Nodes are the primary building blocks of a diagram. They represent entities, processes, decisions, and other visual elements.
Key Node Properties
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
// 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
// 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 methodaddNode()does not exist. Useadd()for a single node andaddElements()for multiple nodes.
Remove Nodes
// 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):
{
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 forwidth,height,offsetX, andoffsetYare not supported. These properties must be numbers (pixels). Responsive sizing must be handled via container resizing and manual recalculation.
Maintaining Aspect Ratio
{
id: 'node1',
width: 100,
height: 100,
shape: { type: 'Image', source: 'image.png' },
constraints: NodeConstraints.Default & ~NodeConstraints.Resize // Prevent resize
}Node Shapes
Flow Chart Shapes
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
shape: { type: 'Basic', shape: 'Rectangle' }
// or: Ellipse, Hexagon, Pentagon, Triangle, Star, Cylinder, ParallelogramPath Shapes
shape: {
type: 'Path',
data: 'M20 20 L20 500 L500 500 L500 20 Z' // SVG path data
}Image Shapes
shape: {
type: 'Image',
source: 'https://example.com/avatar.png',
scale: 'Stretch' // or: Meet, Slice, None
}HTML Shapes
shape: {
type: 'HTML',
content: '<div style="color:red;padding:5px"><b>HTML Node</b></div>'
}NSvg (Native SVG)
shape: {
type: 'Native',
content: '<svg><circle cx="20" cy="20" r="20" fill="blue"/></svg>'
}Styling Nodes
Basic Styling
{
id: 'node1',
style: {
fill: '#90EE90', // Light green
strokeColor: '#228B22', // Dark green
strokeWidth: 2,
opacity: 0.8
}
}Gradients
{
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
{
id: 'node1',
style: {
strokeColor: '#000000',
strokeDashArray: '5,5', // 5px dash, 5px gap
strokeWidth: 2
}
}Shadow Effect
{
id: 'node1',
style: {
fill: '#90EE90',
shadow: {
angle: 45,
blur: 15,
color: 'rgba(0,0,0,0.3)',
distance: 5
}
}
}Node Customization
Custom Templates
<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 section below.
Tooltip on Nodes
{
id: 'node1',
tooltip: {
content: 'This is Node 1',
position: 'TopCenter'
}
}Expand/Collapse
Collapsible Nodes
{
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
diagram.selectionChange((args) => {
if (args.state === 'Changed') {
console.log('Selected nodes:', args.newItems);
console.log('Deselected nodes:', args.oldItems);
}
});Double-click and Click Events
diagram.doubleClick((args) => {
if (args.source instanceof Node) {
console.log('Clicked node:', args.source.id);
}
});Drag Events
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
// 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
// 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:
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
// 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
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
Ports
Table of Contents
- Overview
- Port Types
- Port Positioning
- Port Appearance
- Connecting to Ports
- Port Interaction
- getPortDefaults Pattern
Overview
Ports are connection points on node boundaries where connectors can attach.
Key Properties
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:
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
{
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):
{
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
// 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 edgeMargin from Node Edge
{
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
{
id: 'port1',
offset: { x: 0.5, y: 0 },
shape: 'Circle', // Circle, Square, Triangle
width: 12,
height: 12
}Styling
{
id: 'port1',
offset: { x: 0.5, y: 0 },
style: {
fill: '#FF0000', // Red port
strokeColor: '#000000', // Black border
strokeWidth: 2,
opacity: 0.8
}
}Visibility
{
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
{
id: 'connector1',
sourceID: 'node1',
sourcePortID: 'port1', // Start from specific port
targetID: 'node2',
targetPortID: 'port3' // End at specific port
}Auto-port Connection
{
id: 'connector1',
sourceID: 'node1',
targetID: 'node2'
// Automatically connects to nearest auto-ports
}Change Connection at Runtime
// 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:
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 enabledDraw: Allows drawing connectors from the portDrag: Allows dragging the portToolTip: Shows tooltip on hoverNone: 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:
{
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)
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
// Save complete diagram
const diagramData = diagram.saveDiagram();
console.log(JSON.stringify(diagramData));
// Save to file
const blob = new Blob([JSON.stringify(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
// Load from variable
const diagramData = JSON.parse(jsonString);
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
{
"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
// 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'
};---
Image Export
Export to PNG
// Export entire diagram
diagram.exportDiagram({format: 'PNG', fileName: 'diagram'});
// Export selected area
diagram.exportDiagram({format: 'PNG', fileName: 'diagram', region: 'PageSettings'});Export to SVG
diagram.exportDiagram({format: 'SVG', fileName: 'diagram'});Export to JPG
diagram.exportDiagram({format: 'JPG', fileName: 'diagram'});Export Options
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
diagram.pageSettings = {
width: 1920,
height: 1080,
orientation: 'Landscape'
};
diagram.exportDiagram({format: 'PNG', fileName: 'large-diagram.png'});---
Print Diagram
Print to Printer
diagram.print({region:'Content'});Print with Options
diagram.printSettings = {
pageOrientation: 'Portrait',
multiplePage: false,
region: 'Content'
};
diagram.print(pageOrientation);---
Visio File Import
Import Visio (.vsdx)
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
// 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
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
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
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:
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 parallelogramSequentialData- Stacked boxesPaperTape- Wavy bottom (tape symbol)Sort- Triangle pointing downMultiDocument- Stacked documentsCollate- Hourglass shapeSummingJunction- Circle with crossOr- Curved bow-tieExtract- Trapezoid (extraction)Merge- Inverted trapezoidOfflineStorage- Cylinder (vertical)OnlineStorage- Cylinder (horizontal)ManualInput- Trapezoid pointing downManualOperation- PentagonLoopLimit- Rectangle with arcDelay- Semi-circle (delay)
Basic Shapes
Simple geometric shapes:
shape: { type: 'Basic', shape: 'Rectangle' }Available Basic shapes:
Rectangle- Standard boxEllipse- Circle/ovalHexagon- 6-sided polygonPentagon- 5-sided polygonTriangle- 3-sided polygonStar- 5-pointed starCylinder- 3D cylinderParallelogram- Slanted box
Path (Custom SVG) Shapes
Define custom shapes using SVG path data:
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:
shape: {
type: 'Image',
source: 'https://example.com/avatar.png'
}Scale options:
Stretch- Fit to node size (may distort)Meet- Fit inside without distortionSlice- Fill with croppingNone- Original size
HTML Shapes
Render HTML content as nodes:
shape: {
type: 'HTML',
content: '<div style="background:lightblue;padding:10px;border-radius:5px;"><b>HTML Content</b></div>'
}Native SVG
Embed raw SVG:
shape: {
type: 'Native',
content: '<svg width="100" height="100"><circle cx="50" cy="50" r="40" fill="blue"/></svg>'
}---
Styling Nodes
Basic Colors
{
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
{
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 dashesGradients
{
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
{
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
{
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, navyHex Colors
#FF0000 (Red)
#00FF00 (Green)
#0000FF (Blue)
#FFFF00 (Yellow)
#FF00FF (Magenta)
#00FFFF (Cyan)
#808080 (Gray)RGB/RGBA
fill: 'rgb(255, 0, 0)' // Red
fill: 'rgba(255, 0, 0, 0.5)' // Semi-transparent red---
Theme Customization
Using CSS Variables
// 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
// Override in style
.e-diagram .my-node {
fill: #90EE90;
}---
Text Styling
Font Properties
// 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)
Swimlanes
Table of Contents
- Overview
- Swimlane Structure
- Creating Swimlanes
- Lanes and Phases
- Swimlane Children
- Swimlane Palette
- 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
{
id: 'swimlane1',
shape: {
type: 'SwimLane',
orientation: 'Horizontal',
lanes: [
// Lanes defined here
]
},
width: 800,
height: 200,
offsetX: 400,
offsetY: 200
}Swimlane with Header
{
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
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:
shape: {
type: 'SwimLane',
phases: [
{
id: 'phase1',
header: {
annotation: { content: 'Phase 1' }
}
},
{
id: 'phase2',
header: {
annotation: { content: 'Phase 2' }
}
}
]
}Orientation
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
// 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
// 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
import { SymbolPalette } from '@syncfusion/ej2-angular-diagrams';
Define Swimlane Symbols
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
// Allow resizing (default enabled)
swimlane.constraints = NodeConstraints.Default
// Prevent resizing
swimlane.constraints = NodeConstraints.Default & ~NodeConstraints.ResizeMove Swimlanes
// Allow moving (default enabled)
swimlane.constraints = NodeConstraints.Default
// Prevent moving
swimlane.constraints = NodeConstraints.Default & ~NodeConstraints.DragAdd/Remove Lanes
// 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
// 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)
Symbol Palette
Overview
Symbol Palette provides a draggable library of shapes that users can drag into the diagram.
---
Symbol Palette Setup
Component Template
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
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
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
<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
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
{
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
{
id: 'icon',
shape: {
type: 'Image',
source: 'assets/process-icon.png'
},
width: 60,
height: 60
}Size Configuration
<ejs-symbolpalette
[symbolWidth]="100"
[symbolHeight]="100"
[symbolMargin]="{ left: 5, right: 5, top: 5, bottom: 5 }">
</ejs-symbolpalette>---
Search Functionality
Enable Search
<ejs-symbolpalette
[palettes]="palettes"
[enableSearch]="true">
</ejs-symbolpalette>Search Filter
// 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
palette.selectionChange((args) => {
console.log('Selected symbol:', args.selectedSymbol);
});Drag Started
diagram.dragEnter((args) => {
if (args.source instanceof Symbol) {
console.log('Symbol drag started');
}
});---
Creating Custom Symbol Libraries
Dynamic Symbol Library
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
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)
UML Diagrams
Table of Contents
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
---
Class Diagrams
Class diagrams show the structure and relationships of classes in a system.
Basic Class Node
{
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
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:
classifier: 'Class'Interface Classifier
Shows interface contract:
classifier: 'Interface'Enumeration Classifier
Shows enum values:
classifier: 'Enumeration',
enumMembers: [
{ name: 'RED' },
{ name: 'GREEN' },
{ name: 'BLUE' }
]Package Classifier
Groups related classes:
classifier: 'Package'Component Classifier
Represents a reusable component:
classifier: 'Component'Visibility Modifiers
visibility: 'Public' // + (accessible everywhere)
visibility: 'Private' // - (accessible within class)
visibility: 'Protected' // # (accessible in subclasses)
visibility: 'Package' // ~ (accessible in same package)Abstract Classes
isAbstract: true // Class name appears in italicsStatic Members
attributes: [
{
name: 'count',
isStatic: true // Underlined in diagram
}
]---
UML Relationships
Relationships show how classes are connected:
Association
Generic relationship between classes:
{
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:
{
id: 'generalization',
sourceID: 'subclass',
targetID: 'superclass',
relationship: 'Generalization'
// Shown as unfilled triangle arrow
}Realization (Interface Implementation)
Class implements interface:
{
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):
{
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):
{
id: 'composition',
sourceID: 'ownerClass',
targetID: 'ownedClass',
relationship: 'Composition'
// Shown as filled diamond at owner end
}Dependency
One class depends on another:
{
id: 'dependency',
sourceID: 'class1',
targetID: 'class2',
relationship: 'Dependency'
// Shown as dashed line with arrow
}---
Sequence Diagrams
Sequence diagrams show message interactions between actors and objects over time.
Sequence Diagram Structure
// Actor node
{
id: 'actor1',
width: 50,
height: 50,
offsetX: 100,
offsetY: 60,
shape: { type: 'UmlClassifier', classifier: 'Actor' }
},
// Object node
{
id: 'object1',
width: 100,
height: 30,
offsetX: 300,
offsetY: 60,
shape: { type: 'UmlClassifier', classifier: 'Object' }
},
// Lifeline node
{
id: 'lifeline1',
width: 10,
height: 200,
offsetX: 300,
offsetY: 160,
shape: { type: 'UmlClassifier', classifier: 'Lifeline' },
// Optionally, you can link to the object node via annotations or custom logic
}Shapes
shape: { type: 'UmlClassifier', classifier: 'Actor' } // Stick figure
shape: { type: 'UmlClassifier', classifier: 'Object' } // Rectangle with name
shape: { type: 'UmlClassifier', classifier: 'Lifeline' } // Vertical dashed lineMessages
{
id: 'message1',
sourceID: 'lifeline1',
targetID: 'lifeline2',
type: 'Orthogonal',
shape: {
type: 'UmlMessage',
messageType: 'Synchronous' // Synchronous, Asynchronous, Return
},
annotations: [{
content: 'methodCall()'
}]
}Message Types
messageType: 'Synchronous' // Solid arrow
messageType: 'Asynchronous' // Open arrow
messageType: 'Return' // Dashed arrow
// 'Self' is not a direct messageType, use sourceID and targetID as same for self-messageInteraction Frames
Group messages in constructs:
{
id: 'interactionFrame',
shape: {
type: 'UmlSequence',
shape: 'InteractionUse',
interactionType: 'Loop' // Loop, Opt, Break, Par, Neg, Seq, Strict, Neg, Assert, Ignore, Consider, Neg
}
}---
→ Next: Organize diagrams with [automatic layouts](layouts.md)