
Syncfusion Angular Sankey
- 153 installs
- Updated August 4, 2026
- syncfusion/angular-ui-components-skills
Use syncfusion-angular-sankey for development tasks
About
syncfusion-angular-sankey: A skill for development. This provides functionality for development workflows.
- syncfusion-angular-sankey
Syncfusion Angular Sankey by the numbers
- 153 all-time installs (skills.sh)
- +5 installs in the week ending Jul 27, 2026 (Skillselion tracking)
- Ranked #2,434 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-sankeyAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 153 |
|---|---|
| Last updated | August 4, 2026 |
| Repository | syncfusion/angular-ui-components-skills ↗ |
What it does
Use syncfusion-angular-sankey for development tasks
Files
Implementing Syncfusion Angular Sankey
The Sankey component visualizes directional flow and relationships between entities using nodes and links. Perfect for displaying process flows, energy distribution, resource allocation, and hierarchical relationships with proportional link widths representing quantities.
When to Use This Skill
Use the Sankey component when you need to:
- Visualize Flow Data: Display directional flow with proportional link widths representing quantities
- Show Process Steps: Represent multi-stage processes or workflows
- Analyze Resource Allocation: Visualize how resources move through systems
- Compare Categories: Show relationships and distributions between categories
- Display Hierarchies: Render organizational or hierarchical structures with weighted connections
Component Overview
The Sankey diagram consists of:
- Nodes: Represent sources, sinks, and intermediate entities (customizable width, color, labels)
- Links: Represent flow between nodes (width proportional to value, customizable curvature and color)
- Labels: Display node and link information with configurable positioning and styling
- Legend: Optional legend for categorizing nodes
- Tooltips: Contextual information on hover or click
Documentation and Navigation Guide
API Reference
📄 Read: references/api-reference.md
Getting Started
📄 Read: references/getting-started.md
- Installation of
@syncfusion/ej2-angular-chartspackage - Angular 19+ standalone component setup
- Basic Sankey initialization and first render
- Module injection (Tooltip, Legend services)
- Initial data binding
- When to use: Start here for setup and first working example
Nodes and Links
📄 Read: references/nodes-and-links.md
- Define node structure (id, label, value)
- Customize node appearance (width, padding, colors, opacity)
- Individual node styling and overrides
- Define link relationships between nodes
- Configure link styling (curvature, opacity, color)
- Node offset and positioning
- When to use: Need to configure diagram structure, customize nodes/links, or control positioning
Appearance and Sizing
📄 Read: references/appearance-and-sizing.md
- Set component dimensions (width, height, responsive sizing)
- Background color and border customization
- Margin control
- Theme application (Material, Bootstrap, Tailwind, etc.)
- Global node and link styles
- When to use: Styling the overall diagram, responsive layouts, or theme integration
Labels, Legends, and Tooltips
📄 Read: references/labels-legends-tooltips.md
- Label positioning and styling for nodes and links
- Legend configuration and positioning (top, bottom, left, right)
- Legend highlighting behavior
- Tooltip templates and content customization
- Interactive element behaviors
- When to use: Adding labels, legends, or tooltips for better context
Customization and Styling
📄 Read: references/customization-and-styling.md
- Element-level customization (per-node, per-link styling)
- Predefined color palettes and mapping
- Data-driven conditional styling
- Custom theme creation
- Category-based styling strategies
- Performance optimization for large diagrams
- When to use: Advanced styling, color mapping, or dynamic appearance changes
Events and Interactions
📄 Read: references/events-and-interactions.md
- Lifecycle events (load, loaded)
- Interaction events (nodeClick, nodeEnter, nodeLeave, linkClick)
- Rendering events for dynamic styling (nodeRendering, linkRendering)
- Export and print events
- Event handling patterns
- When to use: Handle user interactions, trigger custom logic, or respond to diagram lifecycle
Advanced Features
📄 Read: references/advanced-features.md
- Horizontal and vertical orientation
- Right-to-left (RTL) layout support
- Print and export functionality (PNG, JPEG, SVG, PDF)
- Accessibility features (WCAG compliance, screen readers)
- Performance optimization for many nodes/links
- Edge cases and troubleshooting
- When to use: Multi-language support, export requirements, accessibility needs, or performance tuning
Quick Start Example
import { Component, ViewEncapsulation } from '@angular/core';
import { SankeyAllModule } from '@syncfusion/ej2-angular-charts';
@Component({
imports: [SankeyAllModule],
standalone: true,
selector: 'app-sankey-demo',
template: `<ejs-sankey [dataSource]="data" [nodeSettings]="nodeSettings"></ejs-sankey>`,
encapsulation: ViewEncapsulation.None
})
export class SankeyDemoComponent {
data: any[] = [
{ sourceID: 'S1', targetID: 'T1', value: 5 },
{ sourceID: 'S1', targetID: 'T2', value: 3 },
{ sourceID: 'S2', targetID: 'T1', value: 2 }
];
nodeSettings: any = {
width: 20,
padding: 10
};
}Common Patterns
Pattern 1: Flow Visualization with Categories
// Define nodes with categories for legend grouping
const nodes = [
{ id: 'Power', label: { text: 'Power Plant' }, category: 'Source' },
{ id: 'Grid', label: { text: 'Grid' }, category: 'Intermediary' },
{ id: 'Home', label: { text: 'Households' }, category: 'Sink' }
];
// Links represent flow quantities
const links = [
{ sourceID: 'Power', targetID: 'Grid', value: 100 },
{ sourceID: 'Grid', targetID: 'Home', value: 80 }
];Pattern 2: Dynamic Styling Based on Values
// Render event handler for value-based coloring
onNodeRendering = (args: INodeRenderingEventArgs) => {
const value = args.node.value || 0;
args.node.color = value > 100 ? '#00A651' : value > 50 ? '#FFB81C' : '#E81B23';
};Pattern 3: Interactive Highlighting
// Use opacity to emphasize relationships
onNodeEnter = (args: INodeEnterEventArgs) => {
args.node.highlightOpacity = 1;
};
onNodeLeave = (args: INodeLeaveEventArgs) => {
args.node.highlightOpacity = 0.3;
};Key Configuration Properties
| Property | Type | Purpose |
|---|---|---|
width | string | Component width ('700px', '100%', or '90%') |
height | string | Component height ('420px', '450px', or '100%') |
title | string | Main title for the diagram |
subTitle | string | Subtitle with descriptive text |
orientation | string | Flow direction ('Horizontal' or 'Vertical') |
enableRtl | boolean | Enable right-to-left layout |
theme | string | Built-in theme (Material, Bootstrap, Tailwind, HighContrast, etc.) |
nodeStyle | Object | Global node styling (width, padding, opacity, colors) |
linkStyle | Object | Global link styling (curvature, opacity, colorType) |
labelSettings | Object | Label positioning and visibility |
legendSettings | Object | Legend configuration and positioning |
tooltip | Object | Tooltip template and visibility settings |
nodes | Array | Node definitions (manual property binding) |
links | Array | Link definitions (manual property binding) |
Common Use Cases
1. Energy Distribution: Show power flow from generation (Solar, Nuclear, Natural Gas) to consumption (Residential, Commercial, Industrial) 2. Supply Chain: Visualize product movement through manufacturing → warehouses → distribution centers → retail stores 3. Financial Flow: Display money transfers between sources (revenue, investment) → operations → expenses 4. Process Workflows: Represent multi-stage processes with branching paths and bottleneck analysis 5. User Journeys: Track user movement through application sections (landing → signup → onboarding → features) 6. Resource Allocation: Show how budget or resources are distributed across departments or projects 7. Hierarchical Structures: Visualize organizational or system hierarchies with weighted relationships
--- Related Documentation: See implementing-syncfusion-angular-components for other data visualization components.
Advanced Features
This reference covers advanced Sankey capabilities including orientation, RTL support, export, accessibility, performance optimization, and responsive design patterns.
Table of Contents
- Orientation
- Horizontal Orientation
- Vertical Orientation
- Switching Orientations Dynamically
- Right-to-Left (RTL) Support
- Enable RTL Mode
- RTL with Arabic Content
- Dynamic RTL Toggle
- Print and Export
- Export to PNG
- Export to Multiple Formats
- Print Diagram
- Export with Custom Dimensions
- Responsive Design
- Mobile-Optimized Layouts
- Responsive Height Calculation
- Accessibility
- WCAG Compliance
- Keyboard Navigation
- Color Contrast and Text Alternatives
- Screen Reader Support
- Performance Optimization
- Data Virtualization
- Optimize Rendering Events
- Reduce Visual Complexity
- Loading ON Demand Implementation
- Edge Cases and Troubleshooting
- Empty Data
- Circular References
- Missing Node References
- Layout Issues
Orientation
The Sankey diagram supports both horizontal and vertical flow orientations.
Horizontal Orientation
Nodes flow from left to right Links flow horizontally between nodes. (Default):
@Component({
template: `
<ejs-sankey
orientation="Horizontal">
</ejs-sankey>
`
})
export class AppComponent {
}Vertical Orientation
Nodes flow from top to bottom:
@Component({
template: `
<ejs-sankey
orientation="Vertical">
</ejs-sankey>
`
})
export class AppComponent {
}Switching Orientations Dynamically
@Component({
// app.component.ts
import { Component ,ViewChild} from '@angular/core';
import { SankeyAllModule } from '@syncfusion/ej2-angular-charts';
import {SankeyComponent,
SankeyTooltipService,
SankeyLegendService,
SankeyExportService
} from '@syncfusion/ej2-angular-charts';
@Component({
standalone: true,
selector: 'app-container',
imports: [SankeyAllModule],
providers: [
SankeyTooltipService,
SankeyLegendService,
SankeyExportService
],
template: `
<div>
<div class="controls">
<button (click)="setOrientation('Horizontal')">Horizontal</button>
<button (click)="setOrientation('Vertical')">Vertical</button>
</div>
<ejs-sankey
width="90%"
height="450px"
[orientation]="currentOrientation"
>
<e-sankey-nodes>
<e-sankey-node id="Agricultural Waste"></e-sankey-node>
<e-sankey-node id="Biomass Residues"></e-sankey-node>
<e-sankey-node id="Bio-conversion"></e-sankey-node>
<e-sankey-node id="Liquid Biofuel"></e-sankey-node>
<e-sankey-node id="Electricity"></e-sankey-node>
<e-sankey-node id="Heat"></e-sankey-node>
</e-sankey-nodes>
<e-sankey-links>
<e-sankey-link sourceId="Agricultural Waste" targetId="Bio-conversion" [value]="84.152"></e-sankey-link>
<e-sankey-link sourceId="Biomass Residues" targetId="Bio-conversion" [value]="24.152"></e-sankey-link>
<e-sankey-link sourceId="Bio-conversion" targetId="Liquid Biofuel" [value]="10.597"></e-sankey-link>
<e-sankey-link sourceId="Bio-conversion" targetId="Electricity" [value]="36.862"></e-sankey-link>
<e-sankey-link sourceId="Bio-conversion" targetId="Heat" [value]="60.845"></e-sankey-link>
</e-sankey-links>
</ejs-sankey>
</div>
`,
styles: [`
.controls {
margin-bottom: 20px;
}
button {
padding: 8px 16px;
margin-right: 10px;
background: #007bff;
color: white;
border: none;
border-radius: 4px;
cursor: pointer;
}
button:hover {
background: #0056b3;
}
`]
})
export class AppComponent {
// Initial state
public currentOrientation: string = 'Horizontal';
// Reference to the Sankey component
@ViewChild('sankey') public sankey?: any;
public setOrientation(orientation: string): void {
// Valid values are 'Horizontal' or 'Vertical'
this.currentOrientation = orientation;
// Refresh the component to recalculate the layout
if (this.sankey) {
this.sankey.refresh();
}
}
}
Right-to-Left (RTL) Support
The Sankey component supports right-to-left rendering for multilingual applications.
Enable RTL Mode
@Component({
template: `
<ejs-sankey
enableRtl="true">
</ejs-sankey>
`
})
export class AppComponent {
}RTL with Arabic Content
// app.component.ts
import { Component ,ViewChild} from '@angular/core';
import { SankeyAllModule } from '@syncfusion/ej2-angular-charts';
import {SankeyComponent,
SankeyTooltipService,
SankeyLegendService,
SankeyExportService
} from '@syncfusion/ej2-angular-charts';
@Component({
selector: 'app-container',
standalone: true,
imports: [SankeyAllModule],
providers: [SankeyTooltipService],
template: `
<div class="sankey-wrapper">
<ejs-sankey
id="sankey-rtl"
[nodes]="arabicNodes"
[links]="arabicLinks"
[enableRtl]="true"
width="100%"
height="400px">
</ejs-sankey>
</div>
`,
styles: [`
.sankey-wrapper {
direction: rtl; /* Sets text direction for the container */
font-family: Arial, sans-serif;
}
`]
})
export class AppComponent {
// Use 'nodes' and 'links' properties directly
public arabicNodes = [
{ id: 'النفط' },
{ id: 'الشمس' },
{ id: 'الشبكة' }
];
public arabicLinks = [
{ sourceId: 'النفط', targetId: 'الشبكة', value: 300 },
{ sourceId: 'الشمس', targetId: 'الشبكة', value: 100 }
];
}
Dynamic RTL Toggle
import { Component, ViewChild } from '@angular/core';
import {
SankeyAllModule,
SankeyTooltipService,
SankeyLegendService,
SankeyExportService
} from '@syncfusion/ej2-angular-charts';
@Component({
standalone: true,
selector: 'app-container',
imports: [SankeyAllModule],
providers: [
SankeyTooltipService,
SankeyLegendService,
SankeyExportService
],
template: `
<div [class.rtl-mode]="isRtl" style="padding: 20px; font-family: sans-serif;">
<div style="margin-bottom: 20px;">
<button (click)="toggleRtl()">
Switch to {{ isRtl ? 'LTR' : 'RTL' }}
</button>
</div>
<ejs-sankey
#sankey
id="sankey-container"
width="100%"
height="500px"
[enableRtl]="isRtl"
[tooltip]="{ enable: true }">
<!-- Manual Node Definition -->
<e-sankey-nodes>
<e-sankey-node id="Agricultural Waste"></e-sankey-node>
<e-sankey-node id="Biomass Residues"></e-sankey-node>
<e-sankey-node id="Bio-conversion"></e-sankey-node>
<e-sankey-node id="Liquid Biofuel"></e-sankey-node>
<e-sankey-node id="Electricity"></e-sankey-node>
<e-sankey-node id="Heat"></e-sankey-node>
</e-sankey-nodes>
<!-- Manual Link Definition -->
<e-sankey-links>
<e-sankey-link sourceId="Agricultural Waste" targetId="Bio-conversion" [value]="84.152"></e-sankey-link>
<e-sankey-link sourceId="Biomass Residues" targetId="Bio-conversion" [value]="24.152"></e-sankey-link>
<e-sankey-link sourceId="Bio-conversion" targetId="Liquid Biofuel" [value]="10.597"></e-sankey-link>
<e-sankey-link sourceId="Bio-conversion" targetId="Electricity" [value]="36.862"></e-sankey-link>
<e-sankey-link sourceId="Bio-conversion" targetId="Heat" [value]="60.845"></e-sankey-link>
</e-sankey-links>
</ejs-sankey>
</div>
`,
styles: [`
.rtl-mode {
direction: rtl;
}
button {
padding: 10px 15px;
cursor: pointer;
background: #0078d4;
color: white;
border: none;
border-radius: 4px;
}
`]
})
export class AppComponent {
isRtl = false;
@ViewChild('sankey') sankey: any;
toggleRtl() {
this.isRtl = !this.isRtl;
// Update document direction for label alignment
document.documentElement.dir = this.isRtl ? 'rtl' : 'ltr';
// Refresh the component to apply RTL mirroring
setTimeout(() => {
if (this.sankey) {
this.sankey.refresh();
}
});
}
}
Print and Export
Export diagrams to multiple formats.
Export to PNG
import { Component, ViewChild } from '@angular/core';
import {
SankeyAllModule,
SankeyTooltipService,
SankeyExportService
} from '@syncfusion/ej2-angular-charts';
@Component({
standalone: true,
selector: 'app-root',
imports: [SankeyAllModule],
providers: [SankeyTooltipService, SankeyExportService],
template: `
<div style="padding: 20px;">
<button (click)="exportToPng()" style="margin-bottom: 10px; cursor: pointer;">
Export PNG
</button>
<ejs-sankey
#sankey
id="sankey-container"
width="100%"
height="450px">
<e-sankey-nodes>
<e-sankey-node id="Agricultural Waste"></e-sankey-node>
<e-sankey-node id="Biomass Residues"></e-sankey-node>
<e-sankey-node id="Bio-conversion"></e-sankey-node>
<e-sankey-node id="Liquid Biofuel"></e-sankey-node>
<e-sankey-node id="Electricity"></e-sankey-node>
<e-sankey-node id="Heat"></e-sankey-node>
</e-sankey-nodes>
<e-sankey-links>
<e-sankey-link sourceId="Agricultural Waste" targetId="Bio-conversion" [value]="84.152"></e-sankey-link>
<e-sankey-link sourceId="Biomass Residues" targetId="Bio-conversion" [value]="24.152"></e-sankey-link>
<e-sankey-link sourceId="Bio-conversion" targetId="Liquid Biofuel" [value]="10.597"></e-sankey-link>
<e-sankey-link sourceId="Bio-conversion" targetId="Electricity" [value]="36.862"></e-sankey-link>
<e-sankey-link sourceId="Bio-conversion" targetId="Heat" [value]="60.845"></e-sankey-link>
</e-sankey-links>
</ejs-sankey>
</div>
`
})
export class AppComponent {
// This matches the #sankey reference in the template
@ViewChild('sankey') sankey: any;
exportToPng() {
if (this.sankey) {
// Arguments: Type ('PNG'), FileName, Orientation, AllowDownload
this.sankey.export('PNG', 'sankey-diagram');
}
}
}
Export to Multiple Formats
import { Component, ViewChild } from '@angular/core';
import { SankeyAllModule, SankeyComponent } from '@syncfusion/ej2-angular-charts';
import {
SankeyTooltipService,
SankeyLegendService,
SankeyExportService
} from '@syncfusion/ej2-angular-charts';
@Component({
imports: [SankeyAllModule],
providers: [
SankeyTooltipService,
SankeyLegendService,
SankeyExportService
],
standalone: true,
selector: 'app-container',
template: `
<div class="control-pane">
<div class="control-section" id="sankey-container">
<button (click)="exportPNG()" style="margin-right: 5px;">Export PNG</button>
<button (click)="exportPDF()" style="margin-right: 5px;">Export PDF</button>
<button (click)="exportSVG()" style="margin-bottom: 10px;">Export SVG</button>
<ejs-sankey
#sankeyChart
id="sankey-container"
width="90%"
height="450px">
<e-sankey-nodes>
<e-sankey-node id="Agricultural Waste"></e-sankey-node>
<e-sankey-node id="Biomass Residues"></e-sankey-node>
<e-sankey-node id="Bio-conversion"></e-sankey-node>
<e-sankey-node id="Liquid Biofuel"></e-sankey-node>
<e-sankey-node id="Electricity"></e-sankey-node>
<e-sankey-node id="Heat"></e-sankey-node>
</e-sankey-nodes>
<e-sankey-links>
<e-sankey-link sourceId="Agricultural Waste" targetId="Bio-conversion" [value]="84.152"></e-sankey-link>
<e-sankey-link sourceId="Biomass Residues" targetId="Bio-conversion" [value]="24.152"></e-sankey-link>
<e-sankey-link sourceId="Bio-conversion" targetId="Liquid Biofuel" [value]="10.597"></e-sankey-link>
<e-sankey-link sourceId="Bio-conversion" targetId="Electricity" [value]="36.862"></e-sankey-link>
<e-sankey-link sourceId="Bio-conversion" targetId="Heat" [value]="60.845"></e-sankey-link>
</e-sankey-links>
</ejs-sankey>
</div>
</div>
`
})
export class AppComponent {
@ViewChild('sankeyChart') sankeyChart?: any;
exportPNG() {
if (this.sankeyChart) {
this.sankeyChart.export('PNG', 'Sankey');
}
}
exportPDF() {
if (this.sankeyChart) {
this.sankeyChart.export('PDF', 'Sankey');
}
}
exportSVG() {
if (this.sankeyChart) {
this.sankeyChart.export('SVG', 'Sankey');
}
}
}Print Diagram
import { Component, ViewChild } from '@angular/core';
import {
SankeyAllModule,
SankeyTooltipService,
SankeyExportService // Required for the .print() method
} from '@syncfusion/ej2-angular-charts';
@Component({
standalone: true,
selector: 'app-container',
imports: [SankeyAllModule],
providers: [SankeyTooltipService, SankeyExportService],
template: `
<div style="padding: 20px;">
<button (click)="printDiagram()" style="margin-bottom: 10px; cursor: pointer;">
Print Diagram
</button>
<!-- Template reference #sankey matches @ViewChild -->
<ejs-sankey
#sankey
id="sankey-container"
width="100%"
height="450px">
<e-sankey-nodes>
<e-sankey-node id="Agricultural Waste"></e-sankey-node>
<e-sankey-node id="Biomass Residues"></e-sankey-node>
<e-sankey-node id="Bio-conversion"></e-sankey-node>
<e-sankey-node id="Liquid Biofuel"></e-sankey-node>
<e-sankey-node id="Electricity"></e-sankey-node>
<e-sankey-node id="Heat"></e-sankey-node>
</e-sankey-nodes>
<e-sankey-links>
<e-sankey-link sourceId="Agricultural Waste" targetId="Bio-conversion" [value]="84.152"></e-sankey-link>
<e-sankey-link sourceId="Biomass Residues" targetId="Bio-conversion" [value]="24.152"></e-sankey-link>
<e-sankey-link sourceId="Bio-conversion" targetId="Liquid Biofuel" [value]="10.597"></e-sankey-link>
<e-sankey-link sourceId="Bio-conversion" targetId="Electricity" [value]="36.862"></e-sankey-link>
<e-sankey-link sourceId="Bio-conversion" targetId="Heat" [value]="60.845"></e-sankey-link>
</e-sankey-links>
</ejs-sankey>
</div>
`,
styles: [`
@media print {
/* Hide the button when printing */
button {
display: none;
}
/* Ensure the Sankey container takes full page width */
#sankey-container {
width: 100% !important;
height: auto !important;
}
}
`]
})
export class AppComponent {
@ViewChild('sankey') sankey: any;
printDiagram() {
if (this.sankey) {
// Triggers the browser's print dialog for the component
this.sankey.print();
}
}
}
Export with Custom Dimensions
import { Component, ViewChild } from '@angular/core';
import {
SankeyAllModule,
SankeyTooltipService,
SankeyExportService
} from '@syncfusion/ej2-angular-charts';
@Component({
standalone: true,
selector: 'app-container',
imports: [SankeyAllModule],
providers: [SankeyTooltipService, SankeyExportService],
template: `
<div style="padding: 20px;">
<button (click)="exportCustom('PNG')" style="margin-right: 5px;">Export PNG (Custom Size)</button>
<button (click)="exportCustom('PDF')" style="margin-bottom: 10px;">Export PDF (Custom Size)</button>
<ejs-sankey
#sankey
id="sankey-container"
width="100%"
height="450px">
<e-sankey-nodes>
<e-sankey-node id="Agricultural Waste"></e-sankey-node>
<e-sankey-node id="Biomass Residues"></e-sankey-node>
<e-sankey-node id="Bio-conversion"></e-sankey-node>
<e-sankey-node id="Liquid Biofuel"></e-sankey-node>
<e-sankey-node id="Electricity"></e-sankey-node>
<e-sankey-node id="Heat"></e-sankey-node>
</e-sankey-nodes>
<e-sankey-links>
<e-sankey-link sourceId="Agricultural Waste" targetId="Bio-conversion" [value]="84.152"></e-sankey-link>
<e-sankey-link sourceId="Biomass Residues" targetId="Bio-conversion" [value]="24.152"></e-sankey-link>
<e-sankey-link sourceId="Bio-conversion" targetId="Liquid Biofuel" [value]="10.597"></e-sankey-link>
<e-sankey-link sourceId="Bio-conversion" targetId="Electricity" [value]="36.862"></e-sankey-link>
<e-sankey-link sourceId="Bio-conversion" targetId="Heat" [value]="60.845"></e-sankey-link>
</e-sankey-links>
</ejs-sankey>
</div>
`
})
export class AppComponent {
@ViewChild('sankey') sankey: any;
exportCustom(format: string) {
if (this.sankey) {
// Export with width and height parameters for custom dimensions
// Supported formats: 'PNG', 'PDF', 'SVG', 'JPEG'
this.sankey.export(format, `Sankey-${format}`);
}
}
}
Responsive Design
Mobile-Optimized Layouts
import { Component } from '@angular/core';
import { CommonModule } from '@angular/common';
import {
SankeyAllModule,
SankeyTooltipService
} from '@syncfusion/ej2-angular-charts';
import { Browser } from '@syncfusion/ej2-base';
@Component({
standalone: true,
selector: 'app-container',
imports: [CommonModule, SankeyAllModule],
providers: [SankeyTooltipService],
template: `
<div class="sankey-responsive">
<ejs-sankey
#sankey
id="sankey-container"
[width]="chartWidth"
[height]="chartHeight"
[orientation]="chartOrientation"
[labelSettings]="labelSettings"
[tooltip]="{ enable: true }">
<e-sankey-nodes>
<e-sankey-node id="Agricultural Waste"></e-sankey-node>
<e-sankey-node id="Biomass Residues"></e-sankey-node>
<e-sankey-node id="Bio-conversion"></e-sankey-node>
<e-sankey-node id="Liquid Biofuel"></e-sankey-node>
<e-sankey-node id="Electricity"></e-sankey-node>
<e-sankey-node id="Heat"></e-sankey-node>
</e-sankey-nodes>
<e-sankey-links>
<e-sankey-link sourceId="Agricultural Waste" targetId="Bio-conversion" [value]="84.152"></e-sankey-link>
<e-sankey-link sourceId="Biomass Residues" targetId="Bio-conversion" [value]="24.152"></e-sankey-link>
<e-sankey-link sourceId="Bio-conversion" targetId="Liquid Biofuel" [value]="10.597"></e-sankey-link>
<e-sankey-link sourceId="Bio-conversion" targetId="Electricity" [value]="36.862"></e-sankey-link>
<e-sankey-link sourceId="Bio-conversion" targetId="Heat" [value]="60.845"></e-sankey-link>
</e-sankey-links>
</ejs-sankey>
</div>
`,
styles: [`
.sankey-responsive {
width: 100%;
overflow-x: auto;
}
@media (max-width: 768px) {
.sankey-responsive {
padding: 10px;
}
}
@media (max-width: 480px) {
.sankey-responsive {
padding: 5px;
}
}
`]
})
export class AppComponent {
// Responsive properties based on device type
get chartWidth(): string {
return Browser.isDevice ? '100%' : '90%';
}
get chartHeight(): string {
if (Browser.isDevice) {
return window.innerHeight > 800 ? '600px' : '400px';
}
return '450px';
}
get chartOrientation(): string {
// Use vertical orientation on small screens, horizontal otherwise
return Browser.isDevice ? 'Vertical' : 'Horizontal';
}
get labelSettings(): any {
return {
visible: !Browser.isDevice // Hide labels on mobile for better space usage
};
}
}
Responsive Height Calculation
import { Component, OnInit } from '@angular/core';
import { CommonModule } from '@angular/common';
import {
SankeyAllModule,
SankeyTooltipService
} from '@syncfusion/ej2-angular-charts';
@Component({
standalone: true,
selector: 'app-container',
imports: [CommonModule, SankeyAllModule],
providers: [SankeyTooltipService],
template: `
<div class="container" [style.height.px]="containerHeight">
<ejs-sankey
#sankey
id="sankey-container"
width="100%"
[height]="chartHeight"
[tooltip]="{ enable: true }">
<e-sankey-nodes>
<e-sankey-node id="Agricultural Waste"></e-sankey-node>
<e-sankey-node id="Biomass Residues"></e-sankey-node>
<e-sankey-node id="Bio-conversion"></e-sankey-node>
<e-sankey-node id="Liquid Biofuel"></e-sankey-node>
<e-sankey-node id="Electricity"></e-sankey-node>
<e-sankey-node id="Heat"></e-sankey-node>
</e-sankey-nodes>
<e-sankey-links>
<e-sankey-link sourceId="Agricultural Waste" targetId="Bio-conversion" [value]="84.152"></e-sankey-link>
<e-sankey-link sourceId="Biomass Residues" targetId="Bio-conversion" [value]="24.152"></e-sankey-link>
<e-sankey-link sourceId="Bio-conversion" targetId="Liquid Biofuel" [value]="10.597"></e-sankey-link>
<e-sankey-link sourceId="Bio-conversion" targetId="Electricity" [value]="36.862"></e-sankey-link>
<e-sankey-link sourceId="Bio-conversion" targetId="Heat" [value]="60.845"></e-sankey-link>
</e-sankey-links>
</ejs-sankey>
</div>
`,
styles: [`
.container {
width: 100%;
display: flex;
align-items: center;
justify-content: center;
}
`]
})
export class AppComponent implements OnInit {
chartHeight: string = '450px';
containerHeight: number = 450;
ngOnInit() {
this.calculateResponsiveHeight();
window.addEventListener('resize', () => this.calculateResponsiveHeight());
}
calculateResponsiveHeight() {
const viewportHeight = window.innerHeight;
const headerHeight = 60; // Estimated header height
const paddingHeight = 40;
const calculatedHeight = Math.max(
400, // Minimum height
viewportHeight - headerHeight - paddingHeight
);
this.chartHeight = `${calculatedHeight}px`;
this.containerHeight = calculatedHeight;
}
}
Accessibility
Implement accessibility features for compliance and usability.
WCAG Compliance
import { Component, ViewChild } from '@angular/core';
import { CommonModule } from '@angular/common';
import {
SankeyAllModule,
SankeyTooltipService
} from '@syncfusion/ej2-angular-charts';
@Component({
standalone: true,
selector: 'app-container',
imports: [CommonModule, SankeyAllModule],
providers: [SankeyTooltipService],
template: `
<div style="padding: 20px; font-family: sans-serif;">
<h2>Energy Flow Diagram</h2>
<p>This diagram shows energy distribution from sources to consumers.</p>
<ejs-sankey
#sankey
height="400px"
[tooltip]="tooltipSettings"
ariaLabel="Energy flow Sankey diagram">
<!-- Manual Node Definition -->
<e-sankey-nodes>
<e-sankey-node id="Coal" label="Coal"></e-sankey-node>
<e-sankey-node id="Solar" label="Solar"></e-sankey-node>
<e-sankey-node id="Grid" label="Power Grid"></e-sankey-node>
</e-sankey-nodes>
<!-- Manual Link Definition -->
<e-sankey-links>
<e-sankey-link sourceId="Coal" targetId="Grid" [value]="300"></e-sankey-link>
<e-sankey-link sourceId="Solar" targetId="Grid" [value]="100"></e-sankey-link>
</e-sankey-links>
</ejs-sankey>
<details style="margin-top: 20px;">
<summary style="cursor: pointer; font-weight: bold;">View Data Table</summary>
<table border="1" style="width: 100%; margin-top: 10px; border-collapse: collapse;">
<thead>
<tr style="background: #f4f4f4;">
<th>Source</th>
<th>Target</th>
<th>Value (MW)</th>
</tr>
</thead>
<tbody>
<tr *ngFor="let link of links">
<td>{{ link.sourceId }}</td>
<td>{{ link.targetId }}</td>
<td>{{ link.value }}</td>
</tr>
</tbody>
</table>
</details>
</div>
`
})
export class AppComponent {
// We keep this array purely to drive the HTML data table
links = [
{ sourceId: 'Coal', targetId: 'Grid', value: 300 },
{ sourceId: 'Solar', targetId: 'Grid', value: 100 }
];
public tooltipSettings: object = {
enable: true,
format: '${sourceId} to ${targetId}: <b>${value} MW</b>'
};
}
Keyboard Navigation
import { Component, ViewChild } from '@angular/core';
import { CommonModule } from '@angular/common';
import {
SankeyAllModule,
SankeyTooltipService
} from '@syncfusion/ej2-angular-charts';
@Component({
standalone: true,
selector: 'app-container',
imports: [CommonModule, SankeyAllModule],
providers: [SankeyTooltipService],
template: `
<div style="padding: 20px; font-family: sans-serif;">
<h2>Keyboard Navigable Sankey</h2>
<p>Use <b>Left/Right Arrows</b> to navigate and <b>Enter</b> to select.</p>
<ejs-sankey
#sankey
id="sankey-container"
height="400px"
tabindex="0"
(keyDown)="onKeyDown($event)"
[tooltip]="{ enable: true }">
<e-sankey-nodes>
<e-sankey-node id="Coal" label="Coal"></e-sankey-node>
<e-sankey-node id="Solar" label="Solar"></e-sankey-node>
<e-sankey-node id="Grid" label="Power Grid"></e-sankey-node>
</e-sankey-nodes>
<e-sankey-links>
<e-sankey-link sourceId="Coal" targetId="Grid" [value]="300"></e-sankey-link>
<e-sankey-link sourceId="Solar" targetId="Grid" [value]="100"></e-sankey-link>
</e-sankey-links>
</ejs-sankey>
</div>
`
})
export class AppComponent {
@ViewChild('sankey') sankey: any;
// List of IDs matching the manual nodes above
nodeIds = ['Coal', 'Solar', 'Grid'];
selectedNodeIndex = 0;
onKeyDown(event: any) {
// Syncfusion sends the event object; extract the keyboard event
const key = event.args?.event?.key || event.key;
switch (key) {
case 'ArrowRight':
this.navigateNode(1);
break;
case 'ArrowLeft':
this.navigateNode(-1);
break;
}
}
navigateNode(direction: number) {
const nodeCount = this.nodeIds.length;
this.selectedNodeIndex = (this.selectedNodeIndex + direction + nodeCount) % nodeCount;
// Optional: Log the current focus for accessibility
console.log('Focused Node:', this.nodeIds[this.selectedNodeIndex]);
}
}Color Contrast and Text Alternatives
import { Component } from '@angular/core';
import { CommonModule } from '@angular/common';
import {
SankeyAllModule,
SankeyTooltipService
} from '@syncfusion/ej2-angular-charts';
@Component({
standalone: true,
selector: 'app-container',
imports: [CommonModule, SankeyAllModule],
providers: [SankeyTooltipService],
template: `
<div class="sankey-wrapper">
<h2>Accessible Energy Flow</h2>
<ejs-sankey
#sankey
id="sankey-container"
height="450px"
theme="HighContrast"
[tooltip]="tooltipSettings"
ariaLabel="High contrast energy flow diagram showing Coal and Solar distribution">
<e-sankey-nodes>
<!-- Manual Nodes with WCAG compliant colors -->
<e-sankey-node id="Coal" label="Coal" color="#000000"></e-sankey-node>
<e-sankey-node id="Solar" label="Solar" color="#D4AF37"></e-sankey-node>
<e-sankey-node id="Grid" label="Power Grid" color="#005A9C"></e-sankey-node>
</e-sankey-nodes>
<e-sankey-links>
<e-sankey-link sourceId="Coal" targetId="Grid" [value]="300"></e-sankey-link>
<e-sankey-link sourceId="Solar" targetId="Grid" [value]="100"></e-sankey-link>
</e-sankey-links>
</ejs-sankey>
</div>
`,
styles: [`
.sankey-wrapper {
padding: 20px;
background: #FFFFFF; /* Ensure high contrast against the page */
}
/* Target internal Syncfusion labels for better readability */
:host ::ng-deep .e-sankey-label {
font-size: 14px !important;
font-weight: 600 !important;
fill: #000000 !important; /* Ensure text is legible */
}
`]
})
export class AppComponent {
public tooltipSettings: object = {
enable: true,
// Use manual link property names for the format
format: 'Source: <b>\${sourceId}</b><br/>Target: <b>\${targetId}</b><br/>Value: <b>\${value} MW</b>'
};
}
Screen Reader Support
import { Component } from '@angular/core';
import { CommonModule } from '@angular/common';
import {
SankeyAllModule,
SankeyTooltipService
} from '@syncfusion/ej2-angular-charts';
@Component({
standalone: true,
selector: 'app-container',
imports: [CommonModule, SankeyAllModule],
providers: [SankeyTooltipService],
template: `
<div class="accessible-sankey">
<h2 id="sankey-title">Energy Flow Analysis</h2>
<p id="sankey-description">
This diagram visualizes energy distribution from renewable and non-renewable sources
to residential, commercial, industrial, and transportation sectors.
</p>
<ejs-sankey
#sankey
id="sankey-container"
height="450px"
[ariaLabel]="'Energy flow Sankey diagram: ' + ariaDescription"
[tooltip]="tooltipSettings"
role="img"
aria-describedby="sankey-description">
<e-sankey-nodes>
<e-sankey-node id="Solar" label="Solar" ariaLabel="Solar energy source"></e-sankey-node>
<e-sankey-node id="Nuclear" label="Nuclear" ariaLabel="Nuclear energy source"></e-sankey-node>
<e-sankey-node id="Grid" label="Power Grid" ariaLabel="Central power distribution grid"></e-sankey-node>
<e-sankey-node id="Residential" label="Residential" ariaLabel="Residential consumption sector"></e-sankey-node>
<e-sankey-node id="Commercial" label="Commercial" ariaLabel="Commercial consumption sector"></e-sankey-node>
</e-sankey-nodes>
<e-sankey-links>
<e-sankey-link sourceId="Solar" targetId="Grid" [value]="454" ariaLabel="Solar to Grid: 454 units"></e-sankey-link>
<e-sankey-link sourceId="Nuclear" targetId="Grid" [value]="185" ariaLabel="Nuclear to Grid: 185 units"></e-sankey-link>
<e-sankey-link sourceId="Grid" targetId="Residential" [value]="300" ariaLabel="Grid to Residential: 300 units"></e-sankey-link>
<e-sankey-link sourceId="Grid" targetId="Commercial" [value]="339" ariaLabel="Grid to Commercial: 339 units"></e-sankey-link>
</e-sankey-links>
</ejs-sankey>
<!-- Data Table Alternative for Screen Readers -->
<details style="margin-top: 30px;">
<summary style="cursor: pointer; font-weight: bold; padding: 10px; background: #f5f5f5; border-radius: 4px;">
View Energy Flow Data Table (for screen readers)
</summary>
<table style="width: 100%; margin-top: 15px; border-collapse: collapse;">
<thead>
<tr style="background: #e8e8e8;">
<th style="border: 1px solid #ddd; padding: 8px; text-align: left;">From (Source)</th>
<th style="border: 1px solid #ddd; padding: 8px; text-align: left;">To (Target)</th>
<th style="border: 1px solid #ddd; padding: 8px; text-align: left;">Flow Value (Units)</th>
</tr>
</thead>
<tbody>
<tr *ngFor="let link of energyData">
<td style="border: 1px solid #ddd; padding: 8px;">{{ link.sourceId }}</td>
<td style="border: 1px solid #ddd; padding: 8px;">{{ link.targetId }}</td>
<td style="border: 1px solid #ddd; padding: 8px;">{{ link.value }}</td>
</tr>
</tbody>
</table>
</details>
</div>
`,
styles: [`
.accessible-sankey {
padding: 20px;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
}
h2 {
color: #333;
margin-bottom: 10px;
}
p {
color: #666;
margin-bottom: 20px;
line-height: 1.6;
}
`]
})
export class AppComponent {
energyData = [
{ sourceId: 'Solar', targetId: 'Grid', value: 454 },
{ sourceId: 'Nuclear', targetId: 'Grid', value: 185 },
{ sourceId: 'Grid', targetId: 'Residential', value: 300 },
{ sourceId: 'Grid', targetId: 'Commercial', value: 339 }
];
ariaDescription = 'Shows energy sources (Solar, Nuclear) flowing to grid and distribution sectors';
tooltipSettings = {
enable: true,
format: 'Energy Flow: <b>${sourceId}</b> to <b>${targetId}</b> = <b>${value} units</b>'
};
}
Performance Optimization
Optimize diagrams with many nodes and links.
Data Virtualization
For large datasets, implement pagination or lazy loading. This reduces DOM elements and improves rendering performance:
import { Component, ViewChild, OnInit } from '@angular/core';
import { CommonModule } from '@angular/common';
import {
SankeyAllModule,
SankeyTooltipService
} from '@syncfusion/ej2-angular-charts';
@Component({
standalone: true,
selector: 'app-container',
imports: [CommonModule, SankeyAllModule],
providers: [SankeyTooltipService],
template: `
<div style="padding: 20px; font-family: sans-serif;">
<h2>Lazy Loading Sankey</h2>
<p>Loaded <b>{{ displayedLinks.length }}</b> links. Use the button to simulate "scrolling" or pagination.</p>
<button (click)="loadNextPage()" style="margin-bottom: 10px;">Load More Links</button>
<ejs-sankey
#sankey
id="sankey-container"
height="500px"
[tooltip]="{ enable: true }">
<!-- Nodes are generated based on the current links -->
<e-sankey-nodes>
<e-sankey-node *ngFor="let node of displayedNodes" [id]="node" [label]="node"></e-sankey-node>
</e-sankey-nodes>
<!-- Links are incrementally added -->
<e-sankey-links>
<e-sankey-link
*ngFor="let link of displayedLinks"
[sourceId]="link.sourceId"
[targetId]="link.targetId"
[value]="link.value">
</e-sankey-link>
</e-sankey-links>
</ejs-sankey>
</div>
`
})
export class AppComponent implements OnInit {
@ViewChild('sankey') sankey: any;
allLinks: any[] = []; // Your "Big Data" source
displayedLinks: any[] = []; // Links currently in the DOM
displayedNodes: string[] = []; // Unique nodes derived from links
pageSize = 5;
currentPage = 0;
ngOnInit() {
// 1. Generate dummy "Large" dataset
for (let i = 1; i <= 50; i++) {
this.allLinks.push({
sourceId: `Source ${i}`,
targetId: 'Central Hub',
value: Math.floor(Math.random() * 100) + 10
});
}
// 2. Initial load
this.loadNextPage();
}
loadNextPage() {
const start = this.currentPage * this.pageSize;
const end = start + this.pageSize;
if (start < this.allLinks.length) {
// 1. Get the next batch of links
const newLinks = this.allLinks.slice(start, end);
// 2. Append to displayed links
this.displayedLinks = [...this.displayedLinks, ...newLinks];
// 3. Update unique nodes list (Sankey needs all IDs defined in <e-sankey-nodes>)
const nodes = new Set(this.displayedNodes);
newLinks.forEach(link => {
nodes.add(link.sourceId);
nodes.add(link.targetId);
});
this.displayedNodes = Array.from(nodes);
// 4. Increment page counter
this.currentPage++;
// 5. Force Syncfusion to refresh the layout
if (this.sankey) {
this.sankey.refresh();
}
} else {
console.log('All links have been loaded.');
}
}
}For large datasets, implement pagination or lazy loading:
import { Component, ViewChild, OnInit } from '@angular/core';
import { CommonModule } from '@angular/common';
import {
SankeyAllModule,
SankeyTooltipService
} from '@syncfusion/ej2-angular-charts';
@Component({
standalone: true,
selector: 'app-container',
imports: [CommonModule, SankeyAllModule],
providers: [SankeyTooltipService],
template: `
<div style="padding: 20px; font-family: sans-serif;">
<h2>Lazy Loading Sankey</h2>
<p>Loaded <b>{{ displayedLinks.length }}</b> links. Use the button to simulate "scrolling" or pagination.</p>
<button (click)="loadNextPage()" style="margin-bottom: 10px;">Load More Links</button>
<ejs-sankey
#sankey
id="sankey-container"
height="500px"
[tooltip]="{ enable: true }">
<!-- Nodes are generated based on the current links -->
<e-sankey-nodes>
<e-sankey-node *ngFor="let node of displayedNodes" [id]="node" [label]="node"></e-sankey-node>
</e-sankey-nodes>
<!-- Links are incrementally added -->
<e-sankey-links>
<e-sankey-link
*ngFor="let link of displayedLinks"
[sourceId]="link.sourceId"
[targetId]="link.targetId"
[value]="link.value">
</e-sankey-link>
</e-sankey-links>
</ejs-sankey>
</div>
`
})
export class AppComponent implements OnInit {
@ViewChild('sankey') sankey: any;
allLinks: any[] = []; // Your "Big Data" source
displayedLinks: any[] = []; // Links currently in the DOM
displayedNodes: string[] = []; // Unique nodes derived from links
pageSize = 5;
currentPage = 0;
ngOnInit() {
// 1. Generate dummy "Large" dataset
for (let i = 1; i <= 50; i++) {
this.allLinks.push({
sourceId: `Source ${i}`,
targetId: 'Central Hub',
value: Math.floor(Math.random() * 100) + 10
});
}
// 2. Initial load
this.loadNextPage();
}
loadNextPage() {
const start = this.currentPage * this.pageSize;
const end = start + this.pageSize;
if (start < this.allLinks.length) {
// Add new links to the view
const newBatch = this.allLinks.slice(start, end);
this.displayedLinks = [...this.displayedLinks, ...newBatch];
// Update nodes list to include any new sources/targets
const nodes = new Set(this.displayedNodes);
newBatch.forEach(l => {
nodes.add(l.sourceId);
nodes.add(l.targetId);
});
this.displayedNodes = Array.from(nodes);
this.currentPage++;
// 3. Force Syncfusion to re-calculate layout
setTimeout(() => {
if (this.sankey) this.sankey.refresh();
});
}
}
}
Optimize Rendering Events
Minimize expensive operations in rendering events:
// Inefficient: Expensive calculations on every render
onNodeRendering = (args: any) => {
const expensiveCalculation = this.heavyComputation(args.node);
args.node.color = expensiveCalculation;
};
// Efficient: Precompute and cache values
private colorCache = new Map();
ngOnInit() {
this.precomputeColors();
}
precomputeColors() {
for (const node of this.nodes) {
this.colorCache.set(node.id, this.computeColor(node));
}
}
onNodeRendering = (args: any) => {
args.node.color = this.colorCache.get(args.node.id);
};Reduce Visual Complexity
import { Component } from '@angular/core';
import { FormsModule } from '@angular/forms';
import { CommonModule } from '@angular/common';
import {
SankeyAllModule,
SankeyTooltipService
} from '@syncfusion/ej2-angular-charts';
@Component({
standalone: true,
selector: 'app-container',
imports: [CommonModule, FormsModule, SankeyAllModule],
providers: [SankeyTooltipService],
template: `
<div style="padding: 20px; font-family: sans-serif;">
<div class="controls" style="margin-bottom: 20px; padding: 10px; background: #f9f9f9; border-radius: 5px;">
<label style="cursor: pointer; font-weight: bold;">
<input type="checkbox" [(ngModel)]="showDetails">
Enable Detailed View (Higher Complexity)
</label>
</div>
<ejs-sankey
#sankey
id="sankey-container"
height="450px"
[nodeStyle]="nodeStyle"
[linkStyle]="linkStyle"
[tooltip]="{ enable: true }">
<e-sankey-nodes>
<e-sankey-node id="Coal" label="Coal Source"></e-sankey-node>
<e-sankey-node id="Solar" label="Solar Source"></e-sankey-node>
<e-sankey-node id="Grid" label="Power Grid"></e-sankey-node>
</e-sankey-nodes>
<e-sankey-links>
<e-sankey-link sourceId="Coal" targetId="Grid" [value]="300"></e-sankey-link>
<e-sankey-link sourceId="Solar" targetId="Grid" [value]="100"></e-sankey-link>
</e-sankey-links>
</ejs-sankey>
</div>
`
})
export class AppComponent {
showDetails = false;
// Use 'nodeStyle' to match the component's input property
get nodeStyle() {
return {
width: this.showDetails ? 30 : 10,
padding: this.showDetails ? 20 : 5,
opacity: this.showDetails ? 1 : 0.6
};
}
// Use 'linkStyle' to match the component's input property
get linkStyle() {
return {
opacity: this.showDetails ? 0.6 : 0.2,
// colorType can be 'Source', 'Target', or 'Blend'
colorType: this.showDetails ? 'Blend' : 'Source'
};
}
}
Loading ON Demand Implementation
Implement loading for on-demand data fetching:
import { Component, ViewChild, OnInit } from '@angular/core';
import { CommonModule } from '@angular/common';
import {
SankeyAllModule,
SankeyTooltipService
} from '@syncfusion/ej2-angular-charts';
@Component({
standalone: true,
selector: 'app-container',
imports: [CommonModule, SankeyAllModule],
providers: [SankeyTooltipService],
template: `
<div style="padding: 20px; font-family: sans-serif;">
<h2>On-Demand Data Loading</h2>
<p>Current load: <b>{{ loadedItems }}/{{ totalItems }}</b> items</p>
<button
(click)="loadMoreData()"
[disabled]="isLoading || loadedItems >= totalItems"
style="margin-bottom: 10px; padding: 8px 16px; cursor: pointer;">
{{ isLoading ? 'Loading...' : 'Load More Data' }}
</button>
<ejs-sankey
#sankey
id="sankey-container"
height="500px"
[tooltip]="{ enable: true }">
<e-sankey-nodes>
<e-sankey-node *ngFor="let node of displayedNodes"
[id]="node.id"
[label]="node.label">
</e-sankey-node>
</e-sankey-nodes>
<e-sankey-links>
<e-sankey-link
*ngFor="let link of displayedLinks"
[sourceId]="link.sourceId"
[targetId]="link.targetId"
[value]="link.value">
</e-sankey-link>
</e-sankey-links>
</ejs-sankey>
</div>
`
})
export class AppComponent implements OnInit {
@ViewChild('sankey') sankey: any;
displayedLinks: any[] = [];
displayedNodes: any[] = [];
isLoading = false;
totalItems = 100;
loadedItems = 0;
pageSize = 10;
ngOnInit() {
// Initial load
this.loadMoreData();
}
loadMoreData() {
if (this.isLoading || this.loadedItems >= this.totalItems) return;
this.isLoading = true;
// Simulate API call delay
setTimeout(() => {
const startIndex = this.loadedItems;
const endIndex = Math.min(startIndex + this.pageSize, this.totalItems);
// Simulate fetching data from server
for (let i = startIndex; i < endIndex; i++) {
const sourceId = `Factory_${Math.floor(i / 3) + 1}`;
const targetId = `District_${(i % 3) + 1}`;
this.displayedLinks.push({
sourceId,
targetId,
value: Math.floor(Math.random() * 100) + 50
});
// Add unique nodes
if (!this.displayedNodes.find(n => n.id === sourceId)) {
this.displayedNodes.push({ id: sourceId, label: sourceId });
}
if (!this.displayedNodes.find(n => n.id === targetId)) {
this.displayedNodes.push({ id: targetId, label: targetId });
}
}
this.loadedItems = endIndex;
this.isLoading = false;
// Refresh the component to recalculate layout
setTimeout(() => {
if (this.sankey) this.sankey.refresh();
});
}, 500); // Simulate network delay
}
}
Edge Cases and Troubleshooting
Empty Data
import { Component } from '@angular/core';
import { CommonModule } from '@angular/common';
import {
SankeyAllModule,
SankeyTooltipService
} from '@syncfusion/ej2-angular-charts';
@Component({
standalone: true,
selector: 'app-container',
imports: [CommonModule, SankeyAllModule],
providers: [SankeyTooltipService],
template: `
<div style="padding: 20px; font-family: sans-serif;">
<h2>Energy Distribution</h2>
<!-- Logic to switch between the Chart and the No Data template -->
<div *ngIf="links && links.length > 0; else noData">
<ejs-sankey
#sankey
id="sankey-container"
height="400px"
[tooltip]="{ enable: true }">
<e-sankey-nodes>
<e-sankey-node *ngFor="let node of nodes" [id]="node.id" [label]="node.label"></e-sankey-node>
</e-sankey-nodes>
<e-sankey-links>
<e-sankey-link
*ngFor="let link of links"
[sourceId]="link.sourceId"
[targetId]="link.targetId"
[value]="link.value">
</e-sankey-link>
</e-sankey-links>
</ejs-sankey>
</div>
<!-- No Data Template -->
<ng-template #noData>
<div class="alert" style="padding: 40px; text-align: center; background: #fff3f3; border: 1px solid #ffc1c1; border-radius: 8px;">
<p style="color: #d8000c; font-weight: bold; margin: 0;">
⚠️ No data available to display the Energy Flow diagram.
</p>
<button (click)="loadDemoData()" style="margin-top: 15px; cursor: pointer;">
Load Demo Data
</button>
</div>
</ng-template>
</div>
`
})
export class AppComponent {
// Initially empty to trigger the 'noData' template
nodes: any[] = [];
links: any[] = [];
loadDemoData() {
this.nodes = [
{ id: 'Coal', label: 'Coal' },
{ id: 'Grid', label: 'Power Grid' }
];
this.links = [
{ sourceId: 'Coal', targetId: 'Grid', value: 300 }
];
}
}
Circular References
Ensure data doesn't create circular flows:
validateData(links: any[]): boolean {
const visited = new Set<string>();
const stack = new Set<string>();
// Use the specific property names from your manual links
const hasCycle = (nodeId: string): boolean => {
if (stack.has(nodeId)) return true; // Cycle detected
if (visited.has(nodeId)) return false;
visited.add(nodeId);
stack.add(nodeId);
// Filter based on 'sourceId' and map to 'targetId'
const connected = links
.filter(link => link.sourceId === nodeId)
.map(link => link.targetId);
for (const target of connected) {
if (hasCycle(target)) return true;
}
stack.delete(nodeId);
return false;
};
// Build a unique set of all node IDs involved
const allNodes = new Set([
...links.map(l => l.sourceId),
...links.map(l => l.targetId)
]);
for (const node of allNodes) {
if (!visited.has(node)) {
if (hasCycle(node)) return false; // Found a circular path
}
}
return true; // Data is safe for Sankey
}Missing Node References
validateNodeReferences(links: any[], nodes: any[]): void {
// 1. Create a Set of all valid node IDs
const nodeIds = new Set(nodes.map(n => n.id));
// 2. Iterate through links using manual property names
links.forEach(link => {
if (!nodeIds.has(link.sourceId)) {
console.warn(`⚠️ Rendering Error: Missing source node [${link.sourceId}]. Please add <e-sankey-node id="${link.sourceId}">.`);
}
if (!nodeIds.has(link.targetId)) {
console.warn(`⚠️ Rendering Error: Missing target node [${link.targetId}]. Please add <e-sankey-node id="${link.targetId}">.`);
}
});
}
Layout Issues
import { Component, ViewChild } from '@angular/core';
import { CommonModule } from '@angular/common';
import {
SankeyAllModule,
SankeyTooltipService
} from '@syncfusion/ej2-angular-charts';
import { FormsModule } from '@angular/forms';
@Component({
standalone: true,
selector: 'app-container',
imports: [CommonModule, SankeyAllModule, FormsModule],
providers: [SankeyTooltipService],
template: `
<div style="padding: 20px; font-family: sans-serif;">
<div style="margin-bottom: 20px;">
<label style="margin-right: 20px;">
<strong>Node Width:</strong>
<input type="range" min="5" max="50" [(ngModel)]="nodeWidth" (change)="updateLayout()">
{{ nodeWidth }}px
</label>
<label style="margin-right: 20px;">
<strong>Node Padding:</strong>
<input type="range" min="0" max="50" [(ngModel)]="nodePadding" (change)="updateLayout()">
{{ nodePadding }}px
</label>
<label>
<strong>Node Offset:</strong>
<input type="range" min="-100" max="100" [(ngModel)]="nodeOffset" (change)="updateLayout()">
{{ nodeOffset }}px
</label>
</div>
<ejs-sankey
#sankey
id="sankey-container"
height="500px"
[nodeStyle]="currentNodeStyle"
[tooltip]="{ enable: true }">
<e-sankey-nodes>
<e-sankey-node id="Agricultural Waste" [offset]="nodeOffset"></e-sankey-node>
<e-sankey-node id="Biomass Residues" [offset]="nodeOffset"></e-sankey-node>
<e-sankey-node id="Bio-conversion"></e-sankey-node>
<e-sankey-node id="Liquid Biofuel"></e-sankey-node>
<e-sankey-node id="Electricity"></e-sankey-node>
<e-sankey-node id="Heat"></e-sankey-node>
</e-sankey-nodes>
<e-sankey-links>
<e-sankey-link sourceId="Agricultural Waste" targetId="Bio-conversion" [value]="84.152"></e-sankey-link>
<e-sankey-link sourceId="Biomass Residues" targetId="Bio-conversion" [value]="24.152"></e-sankey-link>
<e-sankey-link sourceId="Bio-conversion" targetId="Liquid Biofuel" [value]="10.597"></e-sankey-link>
<e-sankey-link sourceId="Bio-conversion" targetId="Electricity" [value]="36.862"></e-sankey-link>
<e-sankey-link sourceId="Bio-conversion" targetId="Heat" [value]="60.845"></e-sankey-link>
</e-sankey-links>
</ejs-sankey>
<div style="margin-top: 20px; padding: 10px; background: #f5f5f5; border-radius: 4px;">
<strong>Layout Troubleshooting Tips:</strong>
<ul>
<li>Use <code>offset</code> property to manually position nodes vertically or horizontally</li>
<li>Adjust node width and padding to control node dimensions and spacing</li>
<li>For overlapping nodes, increase padding or adjust offset values</li>
<li>Call <code>sankey.refresh()</code> after dynamic property changes to recalculate layout</li>
<li>Use responsive width/height properties for mobile devices</li>
</ul>
</div>
</div>
`
})
export class AppComponent {
@ViewChild('sankey') sankey: any;
nodeWidth = 20;
nodePadding = 10;
nodeOffset = 0;
get currentNodeStyle() {
return {
width: this.nodeWidth,
padding: this.nodePadding
};
}
updateLayout() {
if (this.sankey) {
this.sankey.refresh();
}
}
}
Common Layout Issues and Solutions:
| Issue | Cause | Solution |
|---|---|---|
| Nodes overlapping | Insufficient padding or poor positioning | Increase padding property or use offset to manually adjust positions |
| Labels cut off | Limited width for node | Increase component width or use labelSettings.visible = false on mobile |
| Horizontal scrolling on mobile | Fixed width is too large | Use responsive width: width="100%" and set max-width |
| Uneven node spacing | Default layout algorithm | Use offset property for manual positioning of specific nodes |
| Links appear too crowded | Many nodes in small area | Increase component height or use vertical orientation on mobile |
| Text too small to read | Limited viewport space | Implement responsive font sizes or zoom controls |
Sankey API Reference
This document summarizes key properties, methods, and events for the SankeyComponent with direct links to the official Syncfusion Angular API anchors.
- Base API index: https://ej2.syncfusion.com/angular/documentation/api/sankey/index-default
Properties (selected)
accessibility— AccessibilityModelallowExport— booleananimation— AnimationModelbackground— stringbackgroundImage— stringborder— BorderModelenableExport— booleanenablePersistence— booleanenableRtl— booleanfocusBorderColor— stringfocusBorderMargin— numberfocusBorderWidth— numberheight— stringlabelSettings— SankeyLabelSettingsModellegendSettings— SankeyLegendSettingsModellinkStyle— SankeyLinkSettingsModellinks— [SankeyLinkModel[]](https://ej2.syncfusion.com/angular/documentation/api/sankey/sankeylinkmodel)locale— stringmargin— MarginModelnodeLayoutMap— { [key: string]: SankeyNodeLayout }nodeStyle— SankeyNodeSettingsModelnodes— [SankeyNodeModel[]](https://ej2.syncfusion.com/angular/documentation/api/sankey/sankeynodemodel)orientation— OrientationsubTitle— stringsubTitleStyle— SankeyTitleStyleModeltheme— ChartThemetitle— stringtitleStyle— SankeyTitleStyleModeltooltip— SankeyTooltipSettingsModelwidth— string
Events (selected)
afterExport— EmitType<IAfterExportEventArgs>beforeExport— SankeyExportEventArgsbeforePrint— SankeyPrintEventArgsexportCompleted— SankeyExportedEventArgslabelRendering— SankeyLabelRenderEventArgslegendItemHover— SankeyLegendItemHoverEventArgslegendItemRendering— SankeyLegendRenderEventArgslinkClick/linkEnter/linkLeave— SankeyLinkEventArgslinkRendering— SankeyLinkRenderEventArgsload/loaded— SankeyLoadedEventArgsnodeClick/nodeEnter/nodeLeave— SankeyNodeEventArgsnodeRendering— SankeyNodeRenderEventArgssizeChanged— SankeySizeChangedEventArgstooltipRendering— SankeyTooltipRenderEventArgs
--- Generated from: https://ej2.syncfusion.com/angular/documentation/api/sankey/index-default
Appearance and Sizing
Table of Contents
- Component Dimensions
- Fixed Dimensions
- Percentage-Based Sizing
- Responsive Sizing
- Background and Border
- Background Color
- Background Image
- Border Customization
- Border Properties
- Examples
- Margins
- Margin Properties
- Use Cases
- Themes
- Dynamic Theme Switching
- Global Styling
- Global Node Styling
- Global Link Styling
- Complete Styling Example
Component Dimensions
Set the Sankey diagram size using width and height properties. These accept CSS values (pixels or percentages).
Fixed Dimensions
Set specific pixel dimensions for consistent sizing:
@Component({
template: `
<ejs-sankey
width="800px"
height="500px">
</ejs-sankey>
`
})
export class AppComponent {
}| Value | Result |
|---|---|
'800px' | 800 pixels wide |
'500px' | 500 pixels high |
null | Default size (usually 100% width, auto height) |
Percentage-Based Sizing
Use percentages for responsive layouts:
@Component({
template: `
<div class="container">
<ejs-sankey
width="100%"
height="100%">
</ejs-sankey>
</div>
`,
styles: [`
.container {
width: 100%;
height: 600px;
}
`]
})
export class AppComponent {
}Responsive Sizing
Use percentage dimensions with CSS media queries for responsive layouts:
import { Component, OnInit } from '@angular/core';
import { SankeyAllModule } from '@syncfusion/ej2-angular-charts';
import { Browser } from '@syncfusion/ej2-base';
@Component({
imports: [
SankeyAllModule
],
standalone: true,
selector: 'app-container',
template: `
<div className="control-pane">
<div className="control-section" id="sankey-container">
<ejs-sankey
width="90%"
[height]="sankeyHeight"
[margin]="sankeyMargin">
<e-sankey-nodes>
<e-sankey-node id="Agricultural Waste"></e-sankey-node>
<e-sankey-node id="Biomass Residues"></e-sankey-node>
<e-sankey-node id="Bio-conversion"></e-sankey-node>
<e-sankey-node id="Liquid Biofuel"></e-sankey-node>
<e-sankey-node id="Electricity"></e-sankey-node>
<e-sankey-node id="Heat"></e-sankey-node>
</e-sankey-nodes>
<e-sankey-links>
<e-sankey-link sourceId="Agricultural Waste" targetId="Bio-conversion" [value]="84.152"></e-sankey-link>
<e-sankey-link sourceId="Biomass Residues" targetId="Bio-conversion" [value]="24.152"></e-sankey-link>
<e-sankey-link sourceId="Bio-conversion" targetId="Liquid Biofuel" [value]="10.597"></e-sankey-link>
<e-sankey-link sourceId="Bio-conversion" targetId="Electricity" [value]="36.862"></e-sankey-link>
<e-sankey-link sourceId="Bio-conversion" targetId="Heat" [value]="60.845"></e-sankey-link>
</e-sankey-links>
</ejs-sankey>
</div>
</div>
`
})
export class AppComponent implements OnInit {
public sankeyHeight: string = '450px';
public sankeyMargin?: Object;
ngOnInit(): void {
this.sankeyHeight = Browser.isDevice ? '600px' : '450px';
this.sankeyMargin = {
left: Browser.isDevice ? 10 : 40,
right: Browser.isDevice ? 10 : 40,
top: 20,
bottom: 20
};
}
}Background and Border
Customize the diagram's background and border appearance.
Background Color
@Component({
template: `
<ejs-sankey
background="#F5F5F5">
</ejs-sankey>
`
})
export class AppComponent {
}Background Image
@Component({
template: `
<ejs-sankey
[dataSource]="data"
backgroundImage="url('assets/pattern.png')">
</ejs-sankey>
`
})
export class AppComponent {
}Border Customization
Configure the diagram border:
@Component({
template: `
<ejs-sankey
[border]="borderSettings">
</ejs-sankey>
`
})
export class AppComponent {
borderSettings = {
color: '#1F4E78', // Border color
width: 2, // Border width in pixels
dashArray: '5,5' // Dashed pattern: 5px dash, 5px gap
};
}Border Properties
| Property | Type | Default | Purpose |
|---|---|---|---|
color | string | '' | Border color (hex, RGB, or named) |
width | number | 1 | Border width in pixels |
dashArray | string | '' | Dash pattern ('5,5' for dashed) |
Examples
// Solid border
border = { color: '#000', width: 2 };
// Dashed border
border = { color: '#999', width: 1, dashArray: '3,3' };
// Dotted border
border = { color: '#CCC', width: 1, dashArray: '1,2' };
// Thick border
border = { color: '#FF0000', width: 4 };Margins
Control spacing around diagram content using margins:
@Component({
template: `
<ejs-sankey
[margin]="marginSettings">
</ejs-sankey>
`
})
export class AppComponent {
marginSettings = {
left: 20,
right: 20,
top: 20,
bottom: 20
};
}Margin Properties
| Property | Type | Default | Purpose |
|---|---|---|---|
left | number | 10 | Left margin in pixels |
right | number | 10 | Right margin in pixels |
top | number | 10 | Top margin in pixels |
bottom | number | 10 | Bottom margin in pixels |
Use Cases
// Large margins for spacious layout
margins = { left: 50, right: 50, top: 50, bottom: 50 };
// Minimal margins for compact layout
margins = { left: 5, right: 5, top: 5, bottom: 5 };
// Asymmetric margins for specific positioning
margins = { left: 30, right: 10, top: 40, bottom: 20 };Themes
Apply built-in themes to the diagram:
@Component({
template: `
<ejs-sankey
[theme]="selectedTheme">
</ejs-sankey>
`
})
export class AppComponent {
selectedTheme = 'Material';
}Dynamic Theme Switching
@Component({
template: `
<div>
<select
[(ngModel)]="selectedTheme"
(ngModelChange)="onThemeChange($event)">
<option value="Material">Material</option>
<option value="Bootstrap">Bootstrap</option>
<option value="Tailwind">Tailwind</option>
<option value="HighContrast">High Contrast</option>
</select>
<ejs-sankey
#sankey
[theme]="selectedTheme">
<e-sankey-nodes>
<e-sankey-node id="Agricultural Waste"></e-sankey-node>
<e-sankey-node id="Biomass Residues"></e-sankey-node>
<e-sankey-node id="Bio-conversion"></e-sankey-node>
<e-sankey-node id="Liquid Biofuel"></e-sankey-node>
<e-sankey-node id="Electricity"></e-sankey-node>
<e-sankey-node id="Heat"></e-sankey-node>
</e-sankey-nodes>
<e-sankey-links>
<e-sankey-link sourceId="Agricultural Waste" targetId="Bio-conversion" [value]="84.152"></e-sankey-link>
<e-sankey-link sourceId="Biomass Residues" targetId="Bio-conversion" [value]="24.152"></e-sankey-link>
<e-sankey-link sourceId="Bio-conversion" targetId="Liquid Biofuel" [value]="10.597"></e-sankey-link>
<e-sankey-link sourceId="Bio-conversion" targetId="Electricity" [value]="36.862"></e-sankey-link>
<e-sankey-link sourceId="Bio-conversion" targetId="Heat" [value]="60.845"></e-sankey-link>
</e-sankey-links>
</ejs-sankey>
</div>
`
})
export class AppComponent {
selectedTheme = 'Tailwind';
@ViewChild('sankey') sankey!: any;
onThemeChange(theme: string) {
this.selectedTheme = theme;
this.sankey.refresh();
}
}Global Styling
Configure appearance for all nodes and links at once.
Global Node Styling
@Component({
template: `
<ejs-sankey
[nodeStyle]="nodeSettings">
</ejs-sankey>
`
})
export class AppComponent {
nodeSettings = {
width: 25,
padding: 15,
fill: '#4472C4',
stroke: '#1F4E78',
strokeWidth: 2,
opacity: 0.8,
highlightOpacity: 1,
inactiveOpacity: 0.4
};
}Global Link Styling
@Component({
template: `
<ejs-sankey
[linkStyle]="linkSettings">
</ejs-sankey>
`
})
export class AppComponent {
linkSettings = {
opacity: 0.5,
curvature: 0.7,
highlightOpacity: 1,
inactiveOpacity: 0.2
};
}Complete Styling Example
@Component({
template: `
<ejs-sankey
width="100%"
height="600px"
background="#F8F9FA"
[border]="borderSettings"
[margin]="marginSettings"
[theme]="'Material'"
[nodeStyle]="nodeSettings"
[linkStyle]="linkSettings">
<e-sankey-nodes>
<e-sankey-node id="Agricultural Waste"></e-sankey-node>
<e-sankey-node id="Biomass Residues"></e-sankey-node>
<e-sankey-node id="Bio-conversion"></e-sankey-node>
<e-sankey-node id="Liquid Biofuel"></e-sankey-node>
<e-sankey-node id="Electricity"></e-sankey-node>
<e-sankey-node id="Heat"></e-sankey-node>
</e-sankey-nodes>
<e-sankey-links>
<e-sankey-link sourceId="Agricultural Waste" targetId="Bio-conversion" [value]="84.152"></e-sankey-link>
<e-sankey-link sourceId="Biomass Residues" targetId="Bio-conversion" [value]="24.152"></e-sankey-link>
<e-sankey-link sourceId="Bio-conversion" targetId="Liquid Biofuel" [value]="10.597"></e-sankey-link>
<e-sankey-link sourceId="Bio-conversion" targetId="Electricity" [value]="36.862"></e-sankey-link>
<e-sankey-link sourceId="Bio-conversion" targetId="Heat" [value]="60.845"></e-sankey-link>
</e-sankey-links>
</ejs-sankey>
`,
})
export class AppComponent {
borderSettings = {
color: '#1F4E78',
width: 2
};
marginSettings = {
left: 20,
right: 20,
top: 20,
bottom: 20
};
nodeSettings = {
width: 25,
padding: 15,
fill: '#4472C4',
stroke: '#1F4E78',
strokeWidth: 2
};
linkSettings = {
opacity: 0.5,
curvature: 0.7
};
}Customization and Styling
Table of Contents
- Overview
- Element-Level Customization
- Per-Node Customization
- Color Palettes
- Predefined Color Palette
- Gradient Colors
- Conditional Styling
- Value-Based Node Styling
- Link-Based Styling
- Status-Based Styling
- Category-Based Styling
- Category-Based Colors
- Hierarchical Styling
- Advanced Patterns
- Interactive Styling with Hover Effects
- Alternating Node Colors
- Performance Optimization
- Use Global Styles Instead of Per-Item
- Cache Computed Values
- Minimize Event Handlers
- Complete Customization Example
Overview
The Sankey component provides multiple approaches to customize appearance:
- Global Styling: Apply consistent styles via
nodeStyleandlinkStyle - Element-Level Customization: Override styles on individual nodes/links
- Data-Driven Styling: Apply dynamic, data-driven styling
- Theme System: Use or extend predefined themes
Element-Level Customization
Customize specific nodes or links directly in data definitions.
Per-Node Customization
@Component({
template: `
<ejs-sankey>
<e-sankey-nodes>
<e-sankey-node id="Agricultural Waste" color="#f41212"></e-sankey-node>
<e-sankey-node id="Biomass Residues" color="#aed62c"></e-sankey-node>
<e-sankey-node id="Bio-conversion" color="#259bc3"></e-sankey-node>
<e-sankey-node id="Liquid Biofuel" color="#0e11af"></e-sankey-node>
<e-sankey-node id="Electricity" color="#7a0e92"></e-sankey-node>
<e-sankey-node id="Heat" color="#c5b9bb"></e-sankey-node>
</e-sankey-nodes>
<e-sankey-links>
<e-sankey-link sourceId="Agricultural Waste" targetId="Bio-conversion" [value]="84.152"></e-sankey-link>
<e-sankey-link sourceId="Biomass Residues" targetId="Bio-conversion" [value]="24.152"></e-sankey-link>
<e-sankey-link sourceId="Bio-conversion" targetId="Liquid Biofuel" [value]="10.597"></e-sankey-link>
<e-sankey-link sourceId="Bio-conversion" targetId="Electricity" [value]="36.862"></e-sankey-link>
<e-sankey-link sourceId="Bio-conversion" targetId="Heat" [value]="60.845"></e-sankey-link>
</e-sankey-links>
</ejs-sankey>
`
})
export class AppComponent {
}Color Palettes
Apply consistent colors using predefined palettes or custom color schemes.
Predefined Color Palette
@Component({
template: `
<ejs-sankey>
<e-sankey-nodes>
<e-sankey-node id="Agricultural Waste" color="#f41212"></e-sankey-node>
<e-sankey-node id="Biomass Residues" color="#aed62c"></e-sankey-node>
<e-sankey-node id="Bio-conversion" color="#259bc3"></e-sankey-node>
<e-sankey-node id="Liquid Biofuel" color="#0e11af"></e-sankey-node>
<e-sankey-node id="Electricity" color="#7a0e92"></e-sankey-node>
<e-sankey-node id="Heat" color="#c5b9bb"></e-sankey-node>
</e-sankey-nodes>
<e-sankey-links>
<e-sankey-link sourceId="Agricultural Waste" targetId="Bio-conversion" [value]="84.152"></e-sankey-link>
<e-sankey-link sourceId="Biomass Residues" targetId="Bio-conversion" [value]="24.152"></e-sankey-link>
<e-sankey-link sourceId="Bio-conversion" targetId="Liquid Biofuel" [value]="10.597"></e-sankey-link>
<e-sankey-link sourceId="Bio-conversion" targetId="Electricity" [value]="36.862"></e-sankey-link>
<e-sankey-link sourceId="Bio-conversion" targetId="Heat" [value]="60.845"></e-sankey-link>
</e-sankey-links>
</ejs-sankey>
`
})
export class AppComponent {
}Gradient Colors
Create gradient effects for visual hierarchy:
import { Component } from '@angular/core';
import { SankeyAllModule } from '@syncfusion/ej2-angular-charts';
function interpolateColor(start: string, end: string, factor: number): string {
const parseHex = (hex: string) =>
hex.replace('#', '').match(/.{2}/g)!.map(x => parseInt(x, 16));
const [r1, g1, b1] = parseHex(start);
const [r2, g2, b2] = parseHex(end);
const r = Math.round(r1 + (r2 - r1) * factor);
const g = Math.round(g1 + (g2 - g1) * factor);
const b = Math.round(b1 + (b2 - b1) * factor);
return `rgb(${r}, ${g}, ${b})`;
}
function generateGradientColors(
start: string,
end: string,
steps: number
): string[] {
return Array.from({ length: steps }, (_, i) =>
interpolateColor(start, end, i / (steps - 1))
);
}
@Component({
standalone: true,
selector: 'app-container',
imports: [SankeyAllModule],
template: `
<ejs-sankey
width="100%"
height="420px"
title="Gradient Node Colors">
<e-sankey-nodes>
<e-sankey-node id="Node0" [color]="colors[0]" [label]="{ text: 'Node 0' }"></e-sankey-node>
<e-sankey-node id="Node1" [color]="colors[1]" [label]="{ text: 'Node 1' }"></e-sankey-node>
<e-sankey-node id="Node2" [color]="colors[2]" [label]="{ text: 'Node 2' }"></e-sankey-node>
<e-sankey-node id="Node3" [color]="colors[3]" [label]="{ text: 'Node 3' }"></e-sankey-node>
<e-sankey-node id="Node4" [color]="colors[4]" [label]="{ text: 'Node 4' }"></e-sankey-node>
</e-sankey-nodes>
<e-sankey-links>
<e-sankey-link sourceId="Node0" targetId="Node1" [value]="50"></e-sankey-link>
<e-sankey-link sourceId="Node1" targetId="Node2" [value]="40"></e-sankey-link>
<e-sankey-link sourceId="Node2" targetId="Node3" [value]="30"></e-sankey-link>
<e-sankey-link sourceId="Node3" targetId="Node4" [value]="20"></e-sankey-link>
</e-sankey-links>
</ejs-sankey>
`
})
export class AppComponent {
colors = generateGradientColors('#FF0000', '#00FF00', 3);
}Conditional Styling
Apply styles based on data values or conditions using rendering events.
Value-Based Node Styling
oimport { Component } from '@angular/core';
import { SankeyAllModule } from '@syncfusion/ej2-angular-charts';
@Component({
standalone: true,
selector: 'app-container',
imports: [SankeyAllModule],
template: `
<ejs-sankey
height="420px"
title="Conditional Node Styling"
(nodeRendering)="onNodeRendering($event)">
<e-sankey-nodes>
<e-sankey-node id="Source"></e-sankey-node>
<e-sankey-node id="Process"></e-sankey-node>
<e-sankey-node id="Output"></e-sankey-node>
</e-sankey-nodes>
<e-sankey-links>
<e-sankey-link sourceId="Source" targetId="Process" [value]="250"></e-sankey-link>
<e-sankey-link sourceId="Process" targetId="Output" [value]="120"></e-sankey-link>
</e-sankey-links>
</ejs-sankey>
`
})
export class AppComponent {
nodeValueMap: Record<string, number> = {
Source: 250,
Process: 370,
Output: 120
};
onNodeRendering(args: any) {
const value = this.nodeValueMap[args.node.id] || 0;
if (value > 200) {
args.node.color = '#00A651';
args.node.width = 30;
} else if (value > 100) {
args.node.color = '#FFB81C';
args.node.width = 20;
} else {
args.node.color = '#E81B23';
args.node.width = 15;
}
}
}Link-Based Styling
import { Component } from '@angular/core';
import { SankeyAllModule } from '@syncfusion/ej2-angular-charts';
import { SankeyLinkRenderEventArgs } from '@syncfusion/ej2-angular-charts';
@Component({
standalone: true,
selector: 'app-container',
imports: [SankeyAllModule],
template: `
<ejs-sankey
width="100%"
height="420px"
title="Conditional Link Styling"
(linkRendering)="onLinkRendering($event)">
<e-sankey-nodes>
<e-sankey-node id="Source"></e-sankey-node>
<e-sankey-node id="Process"></e-sankey-node>
<e-sankey-node id="Output"></e-sankey-node>
</e-sankey-nodes>
<e-sankey-links>
<e-sankey-link sourceId="Source" targetId="Process" [value]="300"></e-sankey-link>
<e-sankey-link sourceId="Process" targetId="Output" [value]="120"></e-sankey-link>
<e-sankey-link sourceId="Source" targetId="Output" [value]="60"></e-sankey-link>
</e-sankey-links>
</ejs-sankey>
`
})
export class AppComponent {
onLinkRendering = (args: SankeyLinkRenderEventArgs) => {
const maxValue = 1000; // choose your own reference value
const ratio = args.link.value / maxValue;
if (ratio > 0.3) {
args.fill = 'rgba(0, 166, 81, 0.8)'; // Green
} else if (ratio > 0.1) {
args.fill = 'rgba(255, 184, 28, 0.6)'; // Yellow
} else {
args.fill = 'rgba(232, 27, 35, 0.4)'; // Red
}
};
}Status-Based Styling
@Component({
template: `
<ejs-sankey
(nodeRendering)="onNodeRendering($event)">
</ejs-sankey>
`
})
export class AppComponent {
nodeStatus: Record<string, 'active' | 'warning' | 'error'> = {
Node1: 'active',
Node2: 'warning',
Node3: 'error'
};
onNodeRendering = (args: SankeyNodeRenderEventArgs) => {
const status = this.nodeStatus[args.node.id];
switch (status) {
case 'active':
args.node.color = '#00A651';
args.node.opacity = 1;
break;
case 'warning':
args.node.color = '#FFB81C';
args.node.opacity = 0.8;
break;
case 'error':
args.node.color = '#E81B23';
args.node.opacity = 0.6;
break;
default:
args.node.color = '#999';
}
};
}Category-Based Styling
Apply consistent styling to nodes grouped by category.
Category-Based Colors
import { Component } from '@angular/core';
import { SankeyAllModule } from '@syncfusion/ej2-angular-charts';
import { SankeyNodeRenderEventArgs } from '@syncfusion/ej2-angular-charts';
@Component({
standalone: true,
selector: 'app-container',
imports: [SankeyAllModule],
template: `
<ejs-sankey
height="420px"
title="Category-Based Node Styling"
(nodeRendering)="onNodeRendering($event)">
<!-- Nodes -->
<e-sankey-nodes>
<e-sankey-node id="Coal" [label]="{ text: 'Coal' }"></e-sankey-node>
<e-sankey-node id="Oil" [label]="{ text: 'Oil' }"></e-sankey-node>
<e-sankey-node id="Cars" [label]="{ text: 'Cars' }"></e-sankey-node>
<e-sankey-node id="Factory" [label]="{ text: 'Factory' }"></e-sankey-node>
<e-sankey-node id="Home" [label]="{ text: 'Home' }"></e-sankey-node>
</e-sankey-nodes>
<!-- Links -->
<e-sankey-links>
<e-sankey-link sourceId="Coal" targetId="Factory" [value]="200"></e-sankey-link>
<e-sankey-link sourceId="Oil" targetId="Cars" [value]="150"></e-sankey-link>
<e-sankey-link sourceId="Factory" targetId="Home" [value]="300"></e-sankey-link>
</e-sankey-links>
</ejs-sankey>
`
})
export class AppComponent {
// Category → color mapping
categoryColors: Record<string, string> = {
Energy: '#FF6B6B',
Transport: '#4ECDC4',
Industry: '#95E1D3',
Residential: '#FFE66D'
};
// Node → category mapping
nodeCategories: Record<string, string> = {
Coal: 'Energy',
Oil: 'Energy',
Cars: 'Transport',
Factory: 'Industry',
Home: 'Residential'
};
// Apply category-based colors
onNodeRendering(args: SankeyNodeRenderEventArgs) {
const category = this.nodeCategories[args.node.id];
args.node.color = this.categoryColors[category] || '#999';
}
}Hierarchical Styling
@Component({
template: `
<ejs-sankey
height="420px"
title="Hierarchy-Based Node Styling"
(nodeRendering)="onNodeRendering($event)">
<!-- Nodes -->
<e-sankey-nodes>
<e-sankey-node id="Source" [label]="{ text: 'Source' }"></e-sankey-node>
<e-sankey-node id="Processor" [label]="{ text: 'Processor' }"></e-sankey-node>
<e-sankey-node id="Distributor" [label]="{ text: 'Distributor' }"></e-sankey-node>
<e-sankey-node id="Consumer" [label]="{ text: 'Consumer' }"></e-sankey-node>
</e-sankey-nodes>
<!-- Links -->
<e-sankey-links>
<e-sankey-link sourceId="Source" targetId="Processor" [value]="300"></e-sankey-link>
<e-sankey-link sourceId="Processor" targetId="Distributor" [value]="200"></e-sankey-link>
<e-sankey-link sourceId="Distributor" targetId="Consumer" [value]="150"></e-sankey-link>
</e-sankey-links>
</ejs-sankey>
`
})
export class AppComponent {
// Hierarchy level → color mapping
hierarchyColors: Record<string, string> = {
level0: '#1F77B4', // darkest
level1: '#FF7F0E',
level2: '#2CA02C',
level3: '#D62728' // lightest
};
// Node → hierarchy level
nodeLevel: Record<string, string> = {
Source: 'level0',
Processor: 'level1',
Distributor: 'level2',
Consumer: 'level3'
};
// Apply hierarchy-based styling
onNodeRendering(args: SankeyNodeRenderEventArgs) {
const level = this.nodeLevel[args.node.id];
// Color by hierarchy
args.node.color = this.hierarchyColors[level] || '#999';
// Width by hierarchy
if (level === 'level0') {
args.node.width = 30;
} else if (level === 'level1') {
args.node.width = 25;
} else if (level === 'level2') {
args.node.width = 20;
} else {
args.node.width = 15;
}
}
}Advanced Patterns
Interactive Styling with Hover Effects
@Component({
template: `
<ejs-sankey
(nodeEnter)="onNodeEnter($event)"
(nodeLeave)="onNodeLeave($event)">
</ejs-sankey>
`
})
export class AppComponent {
// Store original colors so we can restore them
originalColors: Record<string, string> = {
Source: 'pink',
Process: 'violet',
Output: 'orange'
};
// Hover in
onNodeEnter(args: SankeyNodeEventArgs) {
args.node.color = 'rgba(0, 0, 0, 0.85)'; // highlight color
}
// Hover out
onNodeLeave(args: SankeyNodeEventArgs) {
const nodeId = args.node.id;
args.node.color = this.originalColors[nodeId] || '#999';
}
}Alternating Node Colors
@Component({
template: `
<ejs-sankey
height="420px"
title="Alternating Node Colors"
(nodeRendering)="onNodeRendering($event)">
<e-sankey-nodes>
<e-sankey-node id="NodeA" [label]="{ text: 'Node A' }"></e-sankey-node>
<e-sankey-node id="NodeB" [label]="{ text: 'Node B' }"></e-sankey-node>
<e-sankey-node id="NodeC" [label]="{ text: 'Node C' }"></e-sankey-node>
<e-sankey-node id="NodeD" [label]="{ text: 'Node D' }"></e-sankey-node>
<e-sankey-node id="NodeE" [label]="{ text: 'Node E' }"></e-sankey-node>
</e-sankey-nodes>
<e-sankey-links>
<e-sankey-link sourceId="NodeA" targetId="NodeB" [value]="100"></e-sankey-link>
<e-sankey-link sourceId="NodeB" targetId="NodeC" [value]="80"></e-sankey-link>
<e-sankey-link sourceId="NodeC" targetId="NodeD" [value]="60"></e-sankey-link>
<e-sankey-link sourceId="NodeD" targetId="NodeE" [value]="40"></e-sankey-link>
</e-sankey-links>
</ejs-sankey>
`,
})
export class AppComponent {
colors = ['orange', 'pink', 'yellow'];
nodeOrder = ['NodeA', 'NodeB', 'NodeC', 'NodeD', 'NodeE'];
onNodeRendering(args: SankeyNodeRenderEventArgs) {
const index = this.nodeOrder.indexOf(args.node.id);
if (index !== -1) {
args.node.color = this.colors[index % this.colors.length];
} else {
args.node.color = '#999';
}
}
}Performance Optimization
For diagrams with many nodes and links, optimize rendering performance.
Use Global Styles Instead of Per-Item
Inefficient:
// Styling every node individually in rendering event
onNodeRendering = (args: any) => {
args.node.fill = '#4472C4';
args.node.stroke = '#1F4E78';
args.node.strokeWidth = 2;
// ... expensive calculations
};Efficient:
// 1. Efficient: Define global styles once
// Use nodeStyle and linkStyle to avoid "unknown property" errors
public nodeStyle = {
fill: '#4472C4',
stroke: '#1F4E78',
strokeWidth: 2
};
public linkStyle = {
opacity: 0.3,
colorType: 'Blend'
};
// 2. Conditional: Only override specific nodes by their ID
// Since nodes have "novalue", we target the central hub ID instead
onNodeRendering = (args: any) => {
if (args.node.id === 'Bio-conversion') {
args.node.color = '#ED7D31'; // Override only for this special case
args.node.width = 30; // Highlight the hub physically
}
};Cache Computed Values
import { OnInit } from '@angular/core';
import { Component } from '@angular/core';
import { SankeyAllModule, SankeyTooltipService } from '@syncfusion/ej2-angular-charts';
@Component({
standalone: true,
selector: 'app-container',
imports: [SankeyAllModule],
providers: [SankeyTooltipService],
template: `
<div style="padding: 20px;">
<ejs-sankey
#sankey
height="450px"
(nodeRendering)="onNodeRendering($event)">
<e-sankey-nodes>
<e-sankey-node id="Coal" label="Coal"></e-sankey-node>
<e-sankey-node id="Solar" label="Solar"></e-sankey-node>
<e-sankey-node id="Grid" label="Power Grid"></e-sankey-node>
<e-sankey-node id="City" label="City"></e-sankey-node>
</e-sankey-nodes>
<e-sankey-links>
<e-sankey-link sourceId="Coal" targetId="Grid" [value]="300"></e-sankey-link>
<e-sankey-link sourceId="Solar" targetId="Grid" [value]="100"></e-sankey-link>
<e-sankey-link sourceId="Grid" targetId="City" [value]="400"></e-sankey-link>
</e-sankey-links>
</ejs-sankey>
</div>
`
})
export class AppComponent implements OnInit {
// 1. Your manual link data
private links = [
{ sourceId: 'Agricultural Waste', targetId: 'Bio-conversion', value: 84.152 },
{ sourceId: 'Biomass Residues', targetId: 'Bio-conversion', value: 24.152 },
{ sourceId: 'Bio-conversion', targetId: 'Liquid Biofuel', value: 10.597 }
];
// 2. The Cache
private colorCache = new Map<string, string>();
ngOnInit() {
this.precomputeNodeColors();
}
private precomputeNodeColors() {
// Get unique node IDs from links
const nodeIds = new Set([
...this.links.map(l => l.sourceId),
...this.links.map(l => l.targetId)
]);
nodeIds.forEach(id => {
// Calculate total flow for this ID since 'node' has no value
const incoming = this.links.filter(l => l.targetId === id).reduce((sum, l) => sum + l.value, 0);
const outgoing = this.links.filter(l => l.sourceId === id).reduce((sum, l) => sum + l.value, 0);
const totalFlow = Math.max(incoming, outgoing);
// Store computed color in cache
this.colorCache.set(id, this.getColorByValue(totalFlow));
});
}
private getColorByValue(value: number): string {
if (value > 80) return '#00A651'; // High flow
if (value > 20) return '#FFB81C'; // Medium flow
return '#E81B23'; // Low flow
}
// 3. Rendering Event (Super Fast)
onNodeRendering = (args: any) => {
// Simply pull from the pre-populated cache
if (this.colorCache.has(args.node.id)) {
args.node.color = this.colorCache.get(args.node.id);
}
};
}
Minimize Event Handlers
// Good: Single optimized handler
import { Component } from '@angular/core';
import { SankeyAllModule, SankeyTooltipService } from '@syncfusion/ej2-angular-charts';
@Component({
standalone: true,
selector: 'app-container',
imports: [SankeyAllModule],
providers: [SankeyTooltipService],
template: `
<div style="padding: 20px;">
<ejs-sankey
#sankey
height="450px"
(nodeRendering)="onNodeRendering($event)">
<e-sankey-nodes>
<e-sankey-node id="Coal" label="Coal"></e-sankey-node>
<e-sankey-node id="Solar" label="Solar"></e-sankey-node>
<e-sankey-node id="Grid" label="Power Grid"></e-sankey-node>
<e-sankey-node id="City" label="City"></e-sankey-node>
</e-sankey-nodes>
<e-sankey-links>
<e-sankey-link sourceId="Coal" targetId="Grid" [value]="300"></e-sankey-link>
<e-sankey-link sourceId="Solar" targetId="Grid" [value]="100"></e-sankey-link>
<e-sankey-link sourceId="Grid" targetId="City" [value]="400"></e-sankey-link>
</e-sankey-links>
</ejs-sankey>
</div>
`
})
export class AppComponent {
// Your manual link data
links = [
{ sourceId: 'Coal', targetId: 'Grid', value: 300 },
{ sourceId: 'Solar', targetId: 'Grid', value: 100 },
{ sourceId: 'Grid', targetId: 'City', value: 400 }
];
// Calculate node totals: Source flow + Destination flow
getNodeValue(nodeId: string): number {
const outgoing = this.links.filter(l => l.sourceId === nodeId).reduce((acc, l) => acc + l.value, 0);
const incoming = this.links.filter(l => l.targetId === nodeId).reduce((acc, l) => acc + l.value, 0);
return Math.max(outgoing, incoming); // Total flow through the node
}
onNodeRendering = (args: any) => {
const totalFlow = this.getNodeValue(args.node.id);
if (totalFlow > 200) {
args.node.color = '#00A651'; // Solid green for high volume
args.node.width = 35;
} else {
args.node.color = '#4472C4';
}
};
}
// Avoid: Multiple separate handlers that repeat workComplete Customization Example
import { Component } from '@angular/core';
import { SankeyAllModule, SankeyTooltipService } from '@syncfusion/ej2-angular-charts';
@Component({
standalone: true,
selector: 'app-container',
imports: [SankeyAllModule],
providers: [SankeyTooltipService],
template: `
<div style="padding: 20px;">
<ejs-sankey
#sankey
height="450px"
[nodeStyle]="nodeStyle"
[linkStyle]="linkStyle"
[tooltip]="tooltipSettings"
(nodeRendering)="onNodeRendering($event)"
(linkRendering)="onLinkRendering($event)"
(nodeEnter)="onNodeEnter($event)"
(nodeLeave)="onNodeLeave($event)">
<e-sankey-nodes>
<e-sankey-node id="Coal" label="Coal (Source)"></e-sankey-node>
<e-sankey-node id="Solar" label="Solar (Source)"></e-sankey-node>
<e-sankey-node id="Grid" label="Power Grid"></e-sankey-node>
<e-sankey-node id="City" label="City Center"></e-sankey-node>
</e-sankey-nodes>
<e-sankey-links>
<e-sankey-link sourceId="Coal" targetId="Grid" [value]="300"></e-sankey-link>
<e-sankey-link sourceId="Solar" targetId="Grid" [value]="100"></e-sankey-link>
<e-sankey-link sourceId="Grid" targetId="City" [value]="400"></e-sankey-link>
</e-sankey-links>
</ejs-sankey>
</div>
`,
styles: [`
/* Customizing the labels via CSS since they are SVG elements */
:host ::ng-deep .e-sankey-label {
font-weight: 600;
font-size: 13px !important;
fill: #333 !important;
}
/* Styling the tooltip container */
:host ::ng-deep #sankey-container_toolTip {
border-radius: 8px;
box-shadow: 0 4px 10px rgba(0,0,0,0.2);
}
`]
})
export class AppComponent {
// 1. Enhanced Global Style
nodeStyle = {
width: 30,
padding: 20,
fill: '#4472C4',
opacity: 0.9,
// Add rounded corners to nodes (if supported by the specific version)
cornerRadius: 4
};
linkStyle = {
opacity: 0.3,
curvature: 0.5,
// Links will blend from source node color to target node color
colorType: 'Blend'
};
tooltipSettings = {
enable: true,
format: '${sourceId} → ${targetId} : <b>${value} units</b>'
};
// 2. Data-Driven Node Customization
onNodeRendering = (args: any) => {
// Highlight "Grid" specially as a hub
if (args.node.id === 'Grid') {
args.node.color = '#7030A0'; // Purple hub
} else if (args.node.id === 'City') {
args.node.color = '#00A651'; // Solid Green for high volume
} else {
args.node.color = '#4472C4BB'; // Translucent Blue
}
};
// 3. Link Curvature based on Flow
onLinkRendering = (args: any) => {
if (args.link.value > 300) {
// Make high-value paths slightly straighter
args.link.curvature = 0.3;
}
};
// 4. Interactive Visual Feedback
onNodeEnter = (args: any) => {
// Darken node on hover
args.node.color = '#222';
};
onNodeLeave = (args: any) => {
// Trigger a redraw of the node style
this.onNodeRendering(args);
};
}Getting Started with Sankey
This guide covers installation, setup, and creating your first Sankey diagram in Angular.
Table of Contents
- Getting Started with Sankey
- Installation
- Alternative Installation
- Angular 19+ Standalone Setup
- Basic Sankey Component
- Module Injection (Tooltips & Legend)
- Data Structure
- Running Your First Example
- Common Issues
Installation
Install the Syncfusion Angular Charts package using Angular CLI:
ng add @syncfusion/ej2-angular-chartsThis command automatically:
- Adds
@syncfusion/ej2-angular-chartsand peer dependencies topackage.json - Configures Angular module imports
Alternative Installation
For specific version or ngcc compatibility:
# Standard Ivy package (Angular 16+)
npm install @syncfusion/ej2-angular-charts
# ngcc package (Angular 15 and below only)
npm add @syncfusion/ej2-angular-charts@32.1.19-ngccAngular 19+ Standalone Setup
Modern Angular (19+) uses standalone components by default. Here's the basic setup:
import { Component, ViewEncapsulation } from '@angular/core';
import { SankeyAllModule } from '@syncfusion/ej2-angular-charts';
@Component({
imports: [SankeyAllModule],
standalone: true,
selector: 'app-sankey-container',
template: `<ejs-sankey id="sankey-container"></ejs-sankey>`,
encapsulation: ViewEncapsulation.None
})
export class AppComponent { }Then in main.ts:
import { bootstrapApplication } from '@angular/platform-browser';
import { AppComponent } from './app.component';
import 'zone.js';
bootstrapApplication(AppComponent).catch((err) => console.error(err));Basic Sankey Component
Create a minimal Sankey with data binding:
import { Component, ViewEncapsulation } from '@angular/core';
import { SankeyAllModule } from '@syncfusion/ej2-angular-charts';
@Component({
imports: [SankeyAllModule],
standalone: true,
selector: 'app-sankey-demo',
template: `
<ejs-sankey
id="sankey"
width="100%"
height="420px">
<e-sankey-nodes>
<e-sankey-node id="Oil" [label]="{ text: 'Oil' }"></e-sankey-node>
<e-sankey-node id="Coal" [label]="{ text: 'Coal' }"></e-sankey-node>
<e-sankey-node id="Gas Car" [label]="{ text: 'Gas Car' }"></e-sankey-node>
<e-sankey-node id="Luxury Car" [label]="{ text: 'Luxury Car' }"></e-sankey-node>
<e-sankey-node id="Power Plant" [label]="{ text: 'Power Plant' }"></e-sankey-node>
</e-sankey-nodes>
<e-sankey-links>
<e-sankey-link sourceId="Oil" targetId="Gas Car" [value]="50"></e-sankey-link>
<e-sankey-link sourceId="Oil" targetId="Luxury Car" [value]="20"> </e-sankey-link>
<e-sankey-link sourceId="Coal" targetId="Power Plant" [value]="45"></e-sankey-link>
</e-sankey-links>
</ejs-sankey>
`,
encapsulation: ViewEncapsulation.None
})
export class AppComponent {
}Module Injection (Tooltips & Legend)
To enable tooltips and legend features, inject the required services:
import { Component, ViewEncapsulation } from '@angular/core';
import {
SankeyAllModule,
SankeyTooltipService,
SankeyLegendService
} from '@syncfusion/ej2-angular-charts';
@Component({
imports: [SankeyAllModule],
standalone: true,
providers: [SankeyTooltipService, SankeyLegendService],
selector: 'app-sankey-demo',
template: `
<ejs-sankey>
<e-sankey-nodes>
<e-sankey-node id="Agricultural Waste"></e-sankey-node>
<e-sankey-node id="Biomass Residues"></e-sankey-node>
<e-sankey-node id="Bio-conversion"></e-sankey-node>
<e-sankey-node id="Liquid Biofuel"></e-sankey-node>
<e-sankey-node id="Electricity"></e-sankey-node>
<e-sankey-node id="Heat"></e-sankey-node>
</e-sankey-nodes>
<e-sankey-links>
<e-sankey-link sourceId="Agricultural Waste" targetId="Bio-conversion" [value]="84.152"></e-sankey-link>
<e-sankey-link sourceId="Biomass Residues" targetId="Bio-conversion" [value]="24.152"></e-sankey-link>
<e-sankey-link sourceId="Bio-conversion" targetId="Liquid Biofuel" [value]="10.597"></e-sankey-link>
<e-sankey-link sourceId="Bio-conversion" targetId="Electricity" [value]="36.862"></e-sankey-link>
<e-sankey-link sourceId="Bio-conversion" targetId="Heat" [value]="60.845"></e-sankey-link>
</e-sankey-links>
</ejs-sankey>`,
encapsulation: ViewEncapsulation.None
})
export class AppComponent {
}Data Structure
Sankey charts consists of links connecting nodes:
- Nodes represent the categories, and links represent the flow between them.
import { Component } from '@angular/core';
import { SankeyAllModule } from '@syncfusion/ej2-angular-charts';
import {
SankeyTooltipService,
SankeyLegendService,
SankeyExportService
} from '@syncfusion/ej2-angular-charts';
@Component({
imports: [SankeyAllModule],
providers: [
SankeyTooltipService,
SankeyLegendService,
SankeyExportService
],
standalone: true,
selector: 'app-container',
template: `
<div class="control-pane">
<div class="control-section" id="sankey-container">
<ejs-sankey
width="90%"
height="420px"
title="Energy Flow Diagram">
<!-- nodes -->
<e-sankey-nodes>
<e-sankey-node id="Energy Input" [label]="{ text: 'Energy Input' }"></e-sankey-node>
<e-sankey-node id="Generation" [label]="{ text: 'Generation' }"></e-sankey-node>
<e-sankey-node id="Distribution" [label]="{ text: 'Distribution' }"></e-sankey-node>
<e-sankey-node id="Consumption" [label]="{ text: 'Consumption' }"></e-sankey-node>
</e-sankey-nodes>
<!-- links -->
<e-sankey-links>
<e-sankey-link sourceId="Energy Input" targetId="Generation" [value]="500"></e-sankey-link>
<e-sankey-link sourceId="Generation" targetId="Distribution" [value]="450"></e-sankey-link>
<e-sankey-link sourceId="Distribution" targetId="Consumption" [value]="400"></e-sankey-link>
</e-sankey-links>
</ejs-sankey>
</div>
</div>
`
})
export class AppComponent {}Running Your First Example
1. Create component file with code above 2. Update index.html to include the component selector 3. Run development server:
ng serve4. Open http://localhost:4200 in browser
You should see a Sankey diagram with nodes and flowing links between them.
Common Issues
Issue: Diagram not rendering
- Solution: Ensure
ViewEncapsulation.Noneis set to load default styles
Issue: Nodes not appearing
- Solution: Ensure every sourceId and targetId in <e-sankey-link> exactly matches a node id
Issue: Tooltip not showing
- Solution: Inject
SankeyTooltipServicein providers array
Issue: Legend not visible
- Solution: Inject
SankeyLegendServiceand setlegendSettings.visible = true
Labels, Legends, and Tooltips
Table of Contents
- Node Labels
- Basic Node Labels
- Label Styling
- Hiding Labels
- Legend Configuration
- Enable Legend
- Legend Properties
- Basic Legend Setup
- Legend Positioning
- Position Options
- Legend Size Control
- Responsive Legend Positioning
- Legend Highlighting
- Custom Highlighting Behavior
- Tooltips
- Enable Tooltips
- Default Tooltips
- Tooltip Formatting
- Customizing Tooltip
- Advanced Tooltip Configuration
- Tooltip Rendering Event
- Complete Example
Node Labels
Node labels display text information on diagram nodes.
Basic Node Labels
import { Component, ViewEncapsulation } from '@angular/core';
import { SankeyAllModule } from '@syncfusion/ej2-angular-charts';
@Component({
standalone: true,
selector: 'app-container',
imports: [SankeyAllModule],
encapsulation: ViewEncapsulation.None,
template: `
<ejs-sankey
width="100%"
height="420px"
title="Electricity Flow Diagram"
[labelSettings]="labelSettings">
<!-- Node definitions -->
<e-sankey-nodes>
<e-sankey-node id="Coal" color="#f41212" [label]="label1"> </e-sankey-node>
<e-sankey-node id="Grid" color="#aed62c" [label]="label2"> </e-sankey-node>
<e-sankey-node id="Homes" color="#259bc3" [label]="label3"> </e-sankey-node>
</e-sankey-nodes>
<!-- Link definitions -->
<e-sankey-links>
<e-sankey-link sourceId="Coal" targetId="Grid" [value]="300"></e-sankey-link>
<e-sankey-link sourceId="Grid" targetId="Homes" [value]="300"> </e-sankey-link>
</e-sankey-links>
</ejs-sankey>
`
})
export class AppComponent {
public labelSettings = {
visible: true
};
public label1 = { text:'Coal' }
public label2 = { text:'Grid' }
public label3 = { text:'Homes' }
}Label Styling
Customize label appearance per-node:
public labelSettings = {
visible: true,
fontFamily: 'Times New Roman',
fontStyle: 'italic',
fontWeight: 'bold',
fontSize: '16px',
color: '#C00'
};Hiding Labels
Set labelSettings.visible to false to hide labels when space is limited. Additionally: hiding labels can be useful for creating cleaner visualizations when labels take up too much space.
import { Component } from '@angular/core';
import { SankeyAllModule } from '@syncfusion/ej2-angular-charts';
import { SankeyTooltipService, SankeyLegendService } from '@syncfusion/ej2-angular-charts';
@Component({
standalone: true,
selector: 'app-container',
imports: [SankeyAllModule],
providers: [SankeyTooltipService, SankeyLegendService],
template: `
<div class="control-pane">
<div class="control-section" id="sankey-container">
<ejs-sankey width="90%" height="450px" [labelSettings]="labelSettings">
<e-sankey-nodes>
<e-sankey-node id="Agricultural Waste"></e-sankey-node>
<e-sankey-node id="Biomass Residues"></e-sankey-node>
<e-sankey-node id="Bio-conversion"></e-sankey-node>
<e-sankey-node id="Liquid Biofuel"></e-sankey-node>
<e-sankey-node id="Electricity"></e-sankey-node>
<e-sankey-node id="Heat"></e-sankey-node>
</e-sankey-nodes>
<e-sankey-links>
<e-sankey-link sourceId="Agricultural Waste" targetId="Bio-conversion" [value]="84.152"></e-sankey-link>
<e-sankey-link sourceId="Biomass Residues" targetId="Bio-conversion" [value]="24.152"></e-sankey-link>
<e-sankey-link sourceId="Bio-conversion" targetId="Liquid Biofuel" [value]="10.597"></e-sankey-link>
<e-sankey-link sourceId="Bio-conversion" targetId="Electricity" [value]="36.862"></e-sankey-link>
<e-sankey-link sourceId="Bio-conversion" targetId="Heat" [value]="60.845"></e-sankey-link>
</e-sankey-links>
</ejs-sankey>
</div>
</div>
`
})
export class AppComponent {
public labelSettings = {
visible: false
};
}Legend Configuration
A legend provides a visual key for understanding node categories.
Enable Legend
First, inject the legend service:
import { SankeyAllModule, SankeyLegendService } from '@syncfusion/ej2-angular-charts';
@Component({
imports: [SankeyAllModule],
providers: [SankeyLegendService],
template: `
<ejs-sankey [legendSettings]="legendSettings">
</ejs-sankey>
`
})
export class AppComponent {
legendSettings = {
visible: true
};
}Legend Properties
| Property | Type | Default | Purpose |
|---|---|---|---|
visible | boolean | true | Show/hide legend |
position | string | 'Auto' | Legend position (Auto, Top, Bottom, Left, Right, Custom) |
width | string | null | Legend container width |
height | string | null | Legend container height |
shapeWidth | number | 10 | Legend icon width |
shapeHeight | number | 10 | Legend icon height |
padding | number | 8 | Padding around legend container |
itemPadding | number | null | Padding between items |
shapePadding | number | 8 | Padding between icon and text |
background | string | 'transparent' | Legend background color |
title | string | null | Legend title text |
enableHighlight | boolean | true | Highlight nodes when legend clicked |
isInversed | boolean | false | Inverts the legend layout |
Basic Legend Setup
@Component({
template: `
<ejs-sankey
[legendSettings]="legendSettings">
<e-sankey-nodes>
<e-sankey-node id="Coal" category="Fossil" [label]="{ text: 'Coal' }"></e-sankey-node>
<e-sankey-node id="Solar" category="Renewable" [label]="{ text: 'Solar' }"></e-sankey-node>
<e-sankey-node id="Grid" category="Distribution" [label]="{ text: 'Grid' }"></e-sankey-node>
</e-sankey-nodes>
<!-- Links -->
<e-sankey-links>
<e-sankey-link sourceId="Coal" targetId="Grid" [value]="300"></e-sankey-link>
<e-sankey-link sourceId="Solar" targetId="Grid" [value]="100"></e-sankey-link>
</e-sankey-links>
</ejs-sankey>
`
})
export class AppComponent {
legendSettings = {
visible: true,
title: 'Energy Sources',
enableHighlight: true
};
}Legend Positioning
Control where the legend appears in the diagram.
Position Options
// Top position
legendSettings = { position: 'Top' };
// Bottom position (default for most diagrams)
legendSettings = { position: 'Bottom' };
// Left position
legendSettings = { position: 'Left' };
// Right position
legendSettings = { position: 'Right' };
// Auto position (component chooses based on space)
legendSettings = { position: 'Auto' };
// Custom position (specify exact coordinates)
legendSettings = {
position: 'Custom',
location: { x: 100, y: 100 }
};Legend Size Control
legendSettings = {
visible: true,
position: 'Right',
width: '200px',
height: '400px',
padding: 15,
itemPadding: 10,
shapeWidth: 15,
shapeHeight: 15
};Responsive Legend Positioning
@Component({
template: `
<ejs-sankey
[legendSettings]="legendSettings">
</ejs-sankey>
`,
styles: [`
:host {
display: block;
}
@media (max-width: 768px) {
:host ::ng-deep .legend {
position: bottom;
}
}
`]
})
export class AppComponent {
get legendSettings() {
return {
visible: true,
position: window.innerWidth < 768 ? 'Bottom' : 'Right',
enableHighlight: true
};
}
}Legend Highlighting
Enable interactive highlighting when legend items are hovered:
@Component({
template: `
<ejs-sankey
width="100%"
height="420px"
title="Energy Sources to Grid"
[legendSettings]="legendSettings">
<!-- Nodes -->
<e-sankey-nodes>
<e-sankey-node id="Coal" category="Fossil" [label]="{ text: 'Coal' }"></e-sankey-node>
<e-sankey-node id="Solar" category="Renewable" [label]="{ text: 'Solar' }"></e-sankey-node>
<e-sankey-node id="Wind" category="Renewable" [label]="{ text: 'Wind' }"></e-sankey-node>
<e-sankey-node id="Grid" category="Distribution" [label]="{ text: 'Grid' }"></e-sankey-node>
</e-sankey-nodes>
<!-- Links -->
<e-sankey-links>
<e-sankey-link sourceId="Coal" targetId="Grid" [value]="300"></e-sankey-link>
<e-sankey-link sourceId="Solar" targetId="Grid" [value]="100"></e-sankey-link>
<e-sankey-link sourceId="Wind" targetId="Grid" [value]="80"></e-sankey-link>
</e-sankey-links>
</ejs-sankey>
`
})
export class AppComponent {
// enableHighlight: true = hovering legend item highlights related nodes
legendSettings = {
visible: true,
enableHighlight: true,
position: 'Bottom'
};
}Custom Highlighting Behavior
@Component({
template: `
<ejs-sankey
(legendItemRendering)="onLegendItemRendering($event)">
</ejs-sankey>
`
})
export class AppComponent {
onLegendItemRendering = (args: any) => {
// Custom logic when legend item is rendered
if (args.text === 'Renewable') {
args.fill = '#28A745';
} else {
args.fill = '#999999';
}
};
}Tooltips
Display additional information when hovering over nodes or links.
Enable Tooltips
First, inject the tooltip service:
import { SankeyAllModule, SankeyTooltipService } from '@syncfusion/ej2-angular-charts';
@Component({
imports: [SankeyAllModule],
providers: [SankeyTooltipService],
template: `
<ejs-sankey
[tooltip]="tooltipSettings">
</ejs-sankey>
`
})
export class AppComponent {
tooltipSettings = {
visible: true
};
}Default Tooltips
// Tooltip shows node/link information by default
tooltipSettings = {
visible: true
};Tooltip Formatting
tooltipSettings = {
visible: true,
nodeTemplate: '${name}: ${value}',
linkTemplate: '${start.name}: ${start.out} → ${target.name}: ${target.in}'
};Customizing Tooltip
Adjust tooltip appearance and behavior using tooltip configuration properties:
| Property | Type | Default | Purpose |
|---|---|---|---|
visible | boolean | true | Shows or hides the tooltip |
fill | string | null | Sets the background color of the tooltip |
opacity | number | 0.75 | Controls tooltip transparency (range 0 to 1) |
textStyle | object | null | Configures font size, family, weight, and color of tooltip text |
enableAnimation | boolean | true | Enables or disables tooltip animation |
duration | number | 300 | Tooltip animation duration in milliseconds |
fadeOutDuration | number | 1000 | Tooltip fade-out duration in milliseconds |
import { Component } from '@angular/core';
import { SankeyAllModule } from '@syncfusion/ej2-angular-charts';
import {
SankeyTooltipService,
SankeyLegendService,
SankeyExportService
} from '@syncfusion/ej2-angular-charts';
@Component({
imports: [SankeyAllModule],
providers: [
SankeyTooltipService,
SankeyLegendService,
SankeyExportService
],
standalone: true,
selector: 'app-container',
template: `
<div class="control-pane">
<div class="control-section" id="sankey-container">
<ejs-sankey
width="90%"
height="450px"
[tooltip]="tooltipConfig">
<e-sankey-nodes>
<e-sankey-node id="Agricultural Waste"></e-sankey-node>
<e-sankey-node id="Biomass Residues"></e-sankey-node>
<e-sankey-node id="Bio-conversion"></e-sankey-node>
<e-sankey-node id="Liquid Biofuel"></e-sankey-node>
<e-sankey-node id="Electricity"></e-sankey-node>
<e-sankey-node id="Heat"></e-sankey-node>
</e-sankey-nodes>
<e-sankey-links>
<e-sankey-link sourceId="Agricultural Waste" targetId="Bio-conversion" [value]="84.152"></e-sankey-link>
<e-sankey-link sourceId="Biomass Residues" targetId="Bio-conversion" [value]="24.152"></e-sankey-link>
<e-sankey-link sourceId="Bio-conversion" targetId="Liquid Biofuel" [value]="10.597"></e-sankey-link>
<e-sankey-link sourceId="Bio-conversion" targetId="Electricity" [value]="36.862"></e-sankey-link>
<e-sankey-link sourceId="Bio-conversion" targetId="Heat" [value]="60.845"></e-sankey-link>
</e-sankey-links>
</ejs-sankey>
</div>
</div>`
})
export class AppComponent {
tooltipConfig = {
enable: true,
textStyle: {
fontFamily: 'Arial',
fontStyle: 'normal',
fontWeight: '500',
size: '14px',
color: '#000'
},
fill: '#F3F3F3',
border: {
color: '#666',
width: 1
}
};
}Advanced Tooltip Configuration
Tooltip Rendering Event
Use tooltipRendering to programmatically modify tooltip content before display.
import { Component } from '@angular/core';
import { SankeyAllModule } from '@syncfusion/ej2-angular-charts';
import {
SankeyTooltipService,
SankeyLegendService,
SankeyExportService
} from '@syncfusion/ej2-angular-charts';
// You can put this interface in a separate file (e.g. sankey.interfaces.ts) and import it
export interface SankeyTooltipRenderEventArgs {
node: any; // SankeyNode type (from @syncfusion/ej2-angular-charts)
link: any; // SankeyLink type
text: string;
}
@Component({
imports: [SankeyAllModule],
providers: [
SankeyTooltipService,
SankeyLegendService,
SankeyExportService
],
standalone: true,
selector: 'app-container',
template: `
<div class="control-pane">
<div class="control-section" id="sankey-container">
<ejs-sankey
width="90%"
height="450px"
[tooltip]="{ enable: true }"
(tooltipRender)="onTooltipRender($event)">
<e-sankey-nodes>
<e-sankey-node id="Agricultural Waste"></e-sankey-node>
<e-sankey-node id="Biomass Residues"></e-sankey-node>
<e-sankey-node id="Bio-conversion"></e-sankey-node>
<e-sankey-node id="Liquid Biofuel"></e-sankey-node>
<e-sankey-node id="Electricity"></e-sankey-node>
<e-sankey-node id="Heat"></e-sankey-node>
</e-sankey-nodes>
<e-sankey-links>
<e-sankey-link sourceId="Agricultural Waste" targetId="Bio-conversion" [value]="84.152"></e-sankey-link>
<e-sankey-link sourceId="Biomass Residues" targetId="Bio-conversion" [value]="24.152"></e-sankey-link>
<e-sankey-link sourceId="Bio-conversion" targetId="Liquid Biofuel" [value]="10.597"></e-sankey-link>
<e-sankey-link sourceId="Bio-conversion" targetId="Electricity" [value]="36.862"></e-sankey-link>
<e-sankey-link sourceId="Bio-conversion" targetId="Heat" [value]="60.845"></e-sankey-link>
</e-sankey-links>
</ejs-sankey>
</div>
</div>`
})
export class AppComponent {
onTooltipRender(args: SankeyTooltipRenderEventArgs): void {
if (args.link) {
args.text = `Flow: ${args.link.sourceId} → ${args.link.targetId} (${args.link.value})`;
} else if (args.node) {
args.text = `Node: ${args.node.id}`;
}
}
}Complete Example
@Component({
template: `
<ejs-sankey
height="420px"
title="Energy Distribution"
[legendSettings]="legendSettings"
[tooltip]="tooltipSettings">
<!-- Nodes -->
<e-sankey-nodes>
<e-sankey-node id="Coal" category="Fossil" [label]="{ text: 'Coal' }"></e-sankey-node>
<e-sankey-node id="Solar" category="Renewable" [label]="{ text: 'Solar' }"></e-sankey-node>
<e-sankey-node id="Wind" category="Renewable" [label]="{ text: 'Wind' }"></e-sankey-node>
<e-sankey-node id="Grid" category="Distribution" [label]="{ text: 'Grid' }"></e-sankey-node>
<e-sankey-node id="City" category="Consumer" [label]="{ text: 'City' }"></e-sankey-node>
</e-sankey-nodes>
<!-- Links -->
<e-sankey-links>
<e-sankey-link sourceId="Coal" targetId="Grid" [value]="300"></e-sankey-link>
<e-sankey-link sourceId="Solar" targetId="Grid" [value]="100"></e-sankey-link>
<e-sankey-link sourceId="Wind" targetId="Grid" [value]="80"></e-sankey-link>
<e-sankey-link sourceId="Grid" targetId="City" [value]="480"></e-sankey-link>
</e-sankey-links>
</ejs-sankey>
`
})
export class AppComponent {
public tooltipSettings = {
visible: true,
linkTemplate: '${start.name}: ${start.out} → ${target.name}: ${target.in}'
};
public legendSettings = {
visible: true,
position: 'Bottom',
title: 'Energy Categories',
enableHighlight: true,
padding: 10
};
}