
Syncfusion Angular Chart
- 202 installs
- Updated August 4, 2026
- syncfusion/angular-ui-components-skills
Use syncfusion-angular-chart for development tasks
About
syncfusion-angular-chart: A skill for development. This provides functionality for development workflows.
- syncfusion-angular-chart
Syncfusion Angular Chart by the numbers
- 202 all-time installs (skills.sh)
- +5 installs in the week ending Jul 27, 2026 (Skillselion tracking)
- Ranked #1,942 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-chartAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 202 |
|---|---|
| Last updated | August 4, 2026 |
| Repository | syncfusion/angular-ui-components-skills ↗ |
What it does
Use syncfusion-angular-chart for development tasks
Files
Implementing Syncfusion Angular Chart Component
A comprehensive guide for implementing the Syncfusion Angular Chart component, a high-performance, interactive data visualization library supporting 25+ chart types including line, bar, area, column, pie, financial, and specialized series. Optimized for responsive rendering, smooth interactions, and large datasets.
When to Use This Skill
Use this skill when you need to:
- Visualize Data: Display numerical data as charts, graphs, or plots in Angular applications
- Trend Analysis: Show data trends over time using line, spline, or area charts
- Comparisons: Compare categorical data using column, bar, or grouped charts
- Financial Data: Implement stock charts with candlestick, OHLC, or technical indicators
- Statistical Analysis: Use box plots, histograms, or pareto charts for distributions
- Multi-dimensional Data: Display scatter plots or bubble charts for correlation analysis
- Real-time Monitoring: Create live-updating charts for dashboards or monitoring systems
- Interactive Exploration: Add zooming, panning, crosshair, tooltips, or selection features
- Custom Visualizations: Combine multiple series types, axes, or create complex layouts
- Accessible Charts: Implement WCAG-compliant charts with keyboard navigation and screen reader support
Component Overview
The Syncfusion Angular Chart is a feature-rich data visualization component with:
- 25+ Series Types: Line, spline, step line, area, column, bar, scatter, bubble, pie, financial (candlestick, OHLC), statistical (box & whisker, histogram, pareto), polar, radar, waterfall, and more
- Interactive Features: Zooming (selection, mouse wheel, pinch), panning, tooltips, crosshair, trackball, selection, data editing
- Multiple Axes: Support for primary, secondary, and multiple axes with different data types (numeric, datetime, logarithmic, category)
- Chart Elements: Legends, data markers, data labels, annotations, trendlines, technical indicators, striplines
- Data Binding: Local arrays, remote data (OData, DataManager), JSON sources, dynamic updates
- Customization: Themes, CSS styling, color palettes, responsive design, animations
- Export & Print: Export to PDF, SVG, PNG, JPEG, CSV, XLSX formats
- Accessibility: WCAG compliance, keyboard navigation, ARIA attributes, internationalization, RTL support
Quality Standards for This Skill
This skill documentation follows strict quality standards to ensure accuracy and production-readiness:
✅ API Accuracy Standards
- Property Names: Use exact Syncfusion v33+ API property names (e.g.,
legendSettingsNOTlegend) - Type Safety: All code examples use explicit TypeScript types from
@syncfusion/ej2-angular-charts - Verification: All properties verified against official Syncfusion API docs
- No Assumptions: Never guess property names—always check official documentation first
✅ Code Quality Standards
- TypeScript Strict Mode: All examples compile with
"strict": true - No `any` Types: 100% type coverage, no loose typing
- Tested Examples: Code examples tested in real Angular projects
- Import Statements: All required types explicitly imported
✅ Documentation Standards
- Clear Property Names: Template property names clearly distinguished from TypeScript names
- Type Declarations: TypeScript type models documented for each property
- Common Mistakes Section: Each feature includes "❌ Don't do this" examples
- Related Properties: Links to related settings and cross-references
🔍 Common Pitfalls (Learn From Mistakes)
| ❌ WRONG | ✅ CORRECT | Why |
|---|---|---|
[legend] | [legendSettings] | Must use Settings suffix for config objects |
| No type declared | public legendSettings: LegendSettingsModel = {...} | Type safety enables IDE IntelliSense |
[tooltips] | [tooltip] | Use singular form, not plural |
| Global marker config | Per-series marker config | Markers are series-specific |
See: Property Mapping Guide for complete reference For Developers: Skill Development Guide explains how to maintain this documentation
Documentation and Navigation Guide
Getting Started
📄 Read: references/getting-started.md
When you need to:
- Install and set up the Chart component in an Angular project
- Understand prerequisites and system requirements
- Add the
@syncfusion/ej2-angular-chartspackage - Configure CSS themes and imports
- Create your first basic chart
- Understand the chart module structure
Series Types
📄 Read: references/series-types.md
When you need to:
- Choose the appropriate chart type for your data
- Implement line series (line, step line, spline, stacked, multi-colored)
- Create area charts (area, stacked, 100% stacked, range, spline range)
- Build column and bar charts (column, bar, stacked, range)
- Visualize financial data (candlestick, hilo, hilo-open-close)
- Use statistical charts (box & whisker, histogram, pareto, error bar)
- Create specialized charts (scatter, bubble, polar, radar, waterfall, vertical)
- Combine multiple series types in one chart
Chart Elements
📄 Read: references/chart-elements.md
When you need to:
- Configure and customize legends (positioning, templates, styling)
- Add data markers to highlight points (shapes, sizes, visibility)
- Display data labels on chart elements (positioning, templates, formatting)
- Add custom annotations (text, shapes, images at specific coordinates)
- Include trendlines (linear, exponential, polynomial, moving average)
- Add technical indicators (RSI, MACD, Bollinger Bands, EMA, SMA)
- Highlight specific ranges with striplines
- Apply gradient fills to series
Axes and Layout
📄 Read: references/axes-and-layout.md
When you need to:
- Configure axis types (numeric, datetime, logarithmic, category)
- Set up primary and secondary axes
- Add multiple axes for different data scales
- Format axis labels (rotation, alignment, multilevel labels)
- Customize gridlines and tick marks
- Implement axis crossing and inversed axes
- Create charts with multiple panes
- Configure chart title, subtitle, and dimensions
- Set chart margins and padding
Interactive Features
📄 Read: references/interactive-features.md
When you need to:
- Enable zooming (selection zoom, mouse wheel zoom, pinch zoom, zoom toolbar)
- Implement panning for navigating zoomed charts
- Add tooltips (default, custom templates, shared tooltips)
- Enable crosshair for precise value reading
- Use trackball for multi-series point tracking
- Implement selection (point, series, cluster, drag selection)
- Enable data editing by dragging points
- Synchronize multiple charts for coordinated interactions
Data Binding
📄 Read: references/data-binding.md
When you need to:
- Bind local data (arrays of objects, JSON)
- Connect to remote data sources (OData, RESTful APIs)
- Use DataManager for advanced data operations
- Handle dynamic data updates and real-time data
- Manage empty points and null values
- Map data fields to chart properties
- Optimize performance for large datasets
- Handle asynchronous data loading
Customization
📄 Read: references/customization.md
When you need to:
- Apply built-in themes (Material, Bootstrap, Fabric, Tailwind, etc.)
- Customize chart appearance with CSS
- Use Theme Studio for custom theme generation
- Configure color palettes for series
- Style individual series with custom colors and patterns
- Implement responsive design for different screen sizes
- Configure chart background, border, and area
- Customize animation effects
Accessibility
📄 Read: references/accessibility.md
When you need to:
- Implement WCAG 2.0/2.1 compliant charts
- Enable keyboard navigation (arrow keys, tab, enter)
- Configure ARIA attributes for screen readers
- Ensure proper color contrast and focus indicators
- Support high contrast themes
Property Mapping Guide
📄 Read: references/property-mapping-guide.md
⚠️ START HERE if you:
- Are confused about which properties to use
- Want to understand the
Settingssuffix convention - Need to know which properties are chart-level vs series-level
- Want to avoid common API naming mistakes
- Need TypeScript type information for all properties
Key Resources:
- Complete property mapping table with ✅/❌ comparisons
- Common naming pitfalls (legend vs legendSettings)
- Type-safe implementation patterns
- QA verification checklist for new properties
Skill Development Guide
📄 Read: references/skill-development-guide.md
For Skill Developers & Contributors:
- 4 Key Areas for improving skill documentation
- The
legendSettingscase study (what went wrong, how to fix) - API accuracy verification process
- QA checklist for pre-release documentation
- Common pitfalls to avoid
- Implement internationalization (i18n) for multiple languages
- Enable localization (l10n) for regional formats
- Add RTL (right-to-left) support for appropriate languages
- Configure advanced accessibility features
Advanced Features
📄 Read: references/advanced-features.md
When you need to:
- Handle chart events (load, loaded, pointClick, seriesRender, axisLabelRender, etc.)
- Export charts (PDF, SVG, PNG, JPEG, CSV, XLSX)
- Implement print functionality
- Understand module injection for tree-shaking
- Use different render methods (SVG, Canvas)
- Configure animation settings
- Handle empty point modes
- Access chart API methods programmatically
- Implement advanced event-driven interactions
Common Patterns and Examples
📄 Read: references/common-patterns.md
When you need to:
- See complete examples for common use cases
- Build a trend analysis dashboard
- Create a financial stock chart with technical indicators
- Implement real-time data monitoring with live updates
- Build multi-axis comparison charts
- Create distribution analysis visualizations
- Find troubleshooting solutions for common issues
- Learn performance optimization techniques
- Migrate from EJ1 to EJ2 Chart
- Follow best practices for chart implementation
Quick Start Example
Here's a minimal example to create a basic line chart:
import { ChartModule } from '@syncfusion/ej2-angular-charts'
import { CategoryService, LineSeriesService, DateTimeService } from '@syncfusion/ej2-angular-charts'
import { Component, OnInit } from '@angular/core';
@Component({
imports: [ ChartModule ],
providers: [ CategoryService, LineSeriesService, StepLineSeriesService, SplineSeriesService, StackingLineSeriesService, DateTimeService,
SplineAreaSeriesService, MultiColoredLineSeriesService, ParetoSeriesService, ColumnSeriesService],
standalone: true,
selector: 'app-container',
template: `<ejs-chart id="chart-container" [primaryXAxis]='primaryXAxis'[primaryYAxis]='primaryYAxis'
[title]='title'>
<e-series-collection>
<e-series [dataSource]='chartData' type='Line' xName='month' yName='sales'></e-series>
</e-series-collection>
</ejs-chart>`
})
export class AppComponent implements OnInit {
public chartData?: Object[];
public title?: string;
public primaryXAxis?: Object;
public primaryYAxis?: Object;
ngOnInit(): void {
this.chartData = [
{ month: 'Jan', sales: 35 },
{ month: 'Feb', sales: 28 },
{ month: 'Mar', sales: 34 },
{ month: 'Apr', sales: 32 },
{ month: 'May', sales: 40 },
{ month: 'Jun', sales: 32 },
{ month: 'Jul', sales: 35 },
{ month: 'Aug', sales: 55 },
{ month: 'Sep', sales: 38 },
{ month: 'Oct', sales: 30 },
{ month: 'Nov', sales: 25 },
{ month: 'Dec', sales: 32 }
];
this.primaryXAxis = {
interval: 1,
valueType: 'Category',
};
this.primaryYAxis =
{
title: 'Sales',
},
this.title = 'Monthly Sales Comparison';
}
}Common Patterns
Pattern 1: Multi-Series Comparison Chart
import { ChartModule } from '@syncfusion/ej2-angular-charts'
import { CategoryService, BarSeriesService, ColumnSeriesService, LineSeriesService, LegendService, DataLabelService, MultiLevelLabelService, SelectionService } from '@syncfusion/ej2-angular-charts'
import { Component, OnInit } from '@angular/core';
@Component({
imports: [ ChartModule ],
providers: [ CategoryService, BarSeriesService, ColumnSeriesService, LineSeriesService,LegendService, DataLabelService, MultiLevelLabelService, SelectionService],
standalone: true,
selector: 'app-container',
template: `<ejs-chart id="chart-container" [primaryXAxis]='primaryXAxis'[primaryYAxis]='primaryYAxis' [title]='title'>
<e-series-collection>
<e-series [dataSource]='chartData' type='Column' xName='country' yName='gold' name='gold'></e-series>
<e-series [dataSource]='chartData' type='Column' xName='country' yName='silver' name='Silver'></e-series>
<e-series [dataSource]='chartData' type='Column' xName='country' yName='bronze' name='Bronze'></e-series>
</e-series-collection>
</ejs-chart>`
})
export class AppComponent implements OnInit {
public primaryXAxis?: Object;
public chartData?: Object[];
public title?: string;
public primaryYAxis?: Object;
ngOnInit(): void {
this.chartData = [
{ country: "USA", gold: 50, silver: 70, bronze: 45 },
{ country: "China", gold: 40, silver: 60, bronze: 55 },
{ country: "Japan", gold: 70, silver: 60, bronze: 50 },
{ country: "Australia", gold: 60, silver: 56, bronze: 40 },
{ country: "France", gold: 50, silver: 45, bronze: 35 },
{ country: "Germany", gold: 40, silver: 30, bronze: 22 },
{ country: "Italy", gold: 40, silver: 35, bronze: 37 },
{ country: "Sweden", gold: 30, silver: 25, bronze: 27 }
];
this.primaryXAxis = {
valueType: 'Category',
title: 'Countries'
};
this.primaryYAxis = {
minimum: 0, maximum: 80,
interval: 20, title: 'Medals'
};
this.title = 'Olympic Medals';
}
}Pattern 2: Interactive Chart with Zoom and Tooltip
import { ChartModule } from '@syncfusion/ej2-angular-charts'
import { DateTimeService, StepLineSeriesService, ColumnSeriesService } from '@syncfusion/ej2-angular-charts'
import { LegendService, TooltipService, CategoryService, ZoomService } from '@syncfusion/ej2-angular-charts'
import { Component, OnInit } from '@angular/core';
import { toolData } from './datasource';
@Component({
imports: [ ChartModule ],
providers: [ DateTimeService, ZoomService, StepLineSeriesService, LegendService, TooltipService, CategoryService, ColumnSeriesService],
standalone: true,
selector: 'app-container',
template: `<ejs-chart id="chart-container" [primaryXAxis]='primaryXAxis'[primaryYAxis]='primaryYAxis' [title]='title' [tooltip]='tooltip'>
<e-series-collection>
<e-series [dataSource]='chartData' type='StepLine' xName='x' yName='y' width=2 name='China' [marker]='marker'></e-series>
</e-series-collection>
</ejs-chart>`
})
export class AppComponent implements OnInit {
public primaryXAxis?: Object;
public chartData?: Object[];
public title?: string;
public primaryYAxis?: Object;
public marker?: Object;
public tooltip?: Object;
public zoom?: Object;
ngOnInit(): void {
this.chartData = [
{ x: new Date(1975, 0, 1), y: 16, y1: 10, y2: 4.5 },
{ x: new Date(1980, 0, 1), y: 12.5, y1: 7.5, y2: 5 },
{ x: new Date(1985, 0, 1), y: 19, y1: 11, y2: 6.5 },
{ x: new Date(1990, 0, 1), y: 14.4, y1: 7, y2: 4.4 },
{ x: new Date(1995, 0, 1), y: 11.5, y1: 8, y2: 5 },
{ x: new Date(2000, 0, 1), y: 14, y1: 6, y2: 1.5 },
{ x: new Date(2005, 0, 1), y: 10, y1: 3.5, y2: 2.5 },
{ x: new Date(2010, 0, 1), y: 16, y1: 7, y2: 3.7 }
];
this.primaryXAxis = {
valueType: 'DateTime',
};
this.zoom = {
enableMouseWheelZooming: true,
enablePinchZooming: true,
enableSelectionZooming: true
};
this.tooltip = { enable: true };
this.marker = { visible: true, width: 10, height: 10 };
this.title = 'Unemployment Rates 1975-2010';
}
}Pattern 3: Financial Stock Chart
import { ChartModule } from '@syncfusion/ej2-angular-charts'
import { CategoryService, CandleSeriesService } from '@syncfusion/ej2-angular-charts'
import { Component, OnInit } from '@angular/core';
@Component({
imports: [ ChartModule ],
providers: [CategoryService, CandleSeriesService],
standalone: true,
selector: 'app-container',
template: ` <ejs-chart style='display:block;' id='chart-container' [primaryXAxis]='primaryXAxis' [primaryYAxis]='primaryYAxis' [title]='title' >
<e-series-collection>
<e-series [dataSource]='data' type='Candle' xName='x' high='high' low='low' open='open' close='close' name='SHIRPUR-G'> </e-series>
</e-series-collection>
</ejs-chart>`
})
export class AppComponent implements OnInit {
public primaryXAxis?: Object;
public title?: string;
public primaryYAxis?: Object;
public data?: Object[];
ngOnInit(): void {
this.data = [
{ x: 'Jan', open: 120, high: 160, low: 100, close: 140 },
{ x: 'Feb', open: 150, high: 190, low: 130, close: 170 },
{ x: 'Mar', open: 130, high: 170, low: 110, close: 150 },
{ x: 'Apr', open: 160, high: 180, low: 120, close: 140 },
{ x: 'May', open: 150, high: 170, low: 110, close: 130 }
];
this.primaryXAxis = {
title: 'Date',
valueType: 'Category',
};
this.primaryYAxis = {
title: 'Price', minimum: 100, maximum: 200, interval: 20,
};
this.title = 'Shirpur Gold Refinery Share Price';
}
}Key Configuration Options
Series Configuration
type: Chart type (Line, Column, Bar, Area, Scatter, etc.)dataSource: Data array for the seriesxName,yName: Property names for x and y valuesname: Series name for legendmarker: Marker configuration for data pointsfill: Series color
Axis Configuration
valueType: Axis type (Double, DateTime, Category, Logarithmic)title: Axis title textminimum,maximum: Axis rangeinterval: Label intervallabelFormat: Format string for labelsopposedPosition: Position axis on opposite side
Chart Configuration
title: Chart titlewidth,height: Chart dimensionstheme: Built-in theme namebackground: Chart background colorlegendSettings: Legend configurationzoomSettings: Zoom configurationtooltip: Tooltip configuration
Common Use Cases
1. Business Dashboards: Display KPIs, sales trends, revenue comparisons 2. Financial Applications: Stock charts, portfolio performance, technical analysis 3. Analytics Platforms: User behavior, traffic analysis, conversion funnels 4. Scientific Visualization: Experimental data, statistical distributions, correlation analysis 5. Monitoring Systems: Real-time metrics, system performance, IoT data 6. Reporting Tools: Automated reports, data exports, scheduled visualizations 7. Educational Applications: Math graphs, statistics teaching, data science demonstrations
Next Steps
After creating your basic chart:
- Explore different series types for your specific data visualization needs
- Add interactive features like zooming and tooltips for better user experience
- Customize appearance with themes and styling
- Implement data binding for dynamic data sources
- Ensure accessibility compliance for all users
- Export and print capabilities for sharing visualizations
For detailed implementation guidance, refer to the specific reference files listed in the navigation guide above.
API Reference Documentation
📄 Complete API Guide: references/api-reference.md
The Syncfusion Angular Chart component provides 200+ API documentation files covering:
- Core APIs: ChartModel (40+ properties), AxisModel (50+ properties), SeriesDirective (60+ properties)
- Chart Elements: Legend, Markers, Data Labels, Tooltip, Annotations, Trendlines, Technical Indicators, Striplines
- Interactive Features: Zooming, Panning, Crosshair, Selection, Drag Settings
- Events: 40+ event interfaces (ILoadedEventArgs, IPointRenderEventArgs, IZoomingEventArgs, etc.)
- Enumerations: 50+ enums (ChartSeriesType, ValueType, ChartTheme, SelectionMode, ExportType, etc.)
- Utilities: BorderModel, FontModel, MarginModel, AnimationModel, and more
All API documentation is available at https://ej2.syncfusion.com/angular/documentation/api/chart/. See references/api-reference.md for comprehensive API navigation and documentation structure.
Each reference guide (getting-started.md, series-types.md, axes-and-layout.md, etc.) links to the official online API documentation throughout the content.
Accessibility Reference for Syncfusion Angular Chart
Table of Contents
- Introduction
- WCAG Compliance
- WCAG 2.2 Standards
- Section 508 Compliance
- Keyboard Navigation
- Navigation Keys
- Implementing Keyboard Support
- Screen Reader Support
- ARIA Attributes
- Descriptive Labels
- Color Contrast
- WCAG AA Requirements
- High Contrast Mode
- Internationalization
- Number Formatting
- Date Formatting
- Currency Display
- Localization
- RTL Support
- Translating Text Content
- Testing Accessibility
- Best Practices
- Accessibility Patterns
- Troubleshooting
Introduction
The Syncfusion Angular Chart component is designed with accessibility in mind, following industry standards including ADA, Section 508, WCAG 2.2, and WAI-ARIA guidelines. This ensures that charts are usable by everyone, including people with disabilities who rely on assistive technologies.
This reference guide covers all accessibility features, implementation details, and best practices for creating inclusive data visualizations.
WCAG Compliance
WCAG 2.2 Standards
The chart component meets WCAG 2.2 Level AA requirements:
| Accessibility Criteria | Compliance | Description |
|---|---|---|
| WCAG 2.2 Support | ✅ Full | Meets all Level AA success criteria |
| Section 508 Support | ✅ Full | Compliant with federal accessibility standards |
| Screen Reader Support | ✅ Full | Compatible with JAWS, NVDA, VoiceOver |
| RTL Support | ✅ Full | Right-to-left language support |
| Color Contrast | ✅ Full | Meets 4.5:1 minimum contrast ratio |
| Mobile Device Support | ✅ Full | Touch-friendly and responsive |
| Keyboard Navigation | ✅ Full | Full keyboard control support |
| Accessibility Checker | ✅ Validated | Tested with accessibility-checker |
| Axe-core Validation | ✅ Validated | Passes axe-core accessibility tests |
Section 508 Compliance
The chart component adheres to Section 508 standards:
§ 1194.21 Software Applications and Operating Systems:
- ✅ Keyboard access to all functionality
- ✅ Focus indicators for all interactive elements
- ✅ Alternative text for non-text content
- ✅ Color not used as the only visual means of conveying information
§ 1194.22 Web-based Intranet and Internet Information:
- ✅ Text equivalents for every non-text element
- ✅ Equivalent alternatives for multimedia
- ✅ Organized layout without stylesheets
- ✅ Client-side image maps with text equivalents
Keyboard Navigation
Navigation Keys
The chart supports comprehensive keyboard navigation:
General Navigation:
| Key Combination | Action |
|---|---|
| <kbd>Alt + J</kbd> | Move focus to chart container |
| <kbd>Tab</kbd> | Move to next focusable element |
| <kbd>Shift + Tab</kbd> | Move to previous focusable element |
| <kbd>Enter / Space</kbd> | Activate/select focused element |
| <kbd>Esc</kbd> | Close tooltip or modal overlay |
Series and Data Point Navigation:
| Key | Action |
|---|---|
| <kbd>→</kbd> | Move to next data point |
| <kbd>←</kbd> | Move to previous data point |
| <kbd>↑</kbd> | Move to point above (if applicable) |
| <kbd>↓</kbd> | Move to point below (if applicable) |
| <kbd>Enter / Space</kbd> | Select focused data point |
Legend Navigation:
| Key | Action |
|---|---|
| <kbd>→</kbd> | Move to next legend item |
| <kbd>←</kbd> | Move to previous legend item |
| <kbd>↑</kbd> | Move up one legend item (vertical legend) |
| <kbd>↓</kbd> | Move down one legend item (vertical legend) |
| <kbd>Enter / Space</kbd> | Toggle series visibility |
Zoom and Pan:
| Key | Action |
|---|---|
| <kbd>Ctrl + +</kbd> | Zoom in |
| <kbd>Ctrl + -</kbd> | Zoom out |
| <kbd>←/→</kbd> | Pan horizontally (when enabled) |
| <kbd>↑/↓</kbd> | Pan vertically (when enabled) |
| <kbd>R</kbd> | Reset zoom/pan |
Implementing Keyboard Support
Basic Configuration:
import { ChartModule } from '@syncfusion/ej2-angular-charts';
import { Component } from '@angular/core';
import { DateTimeService, StepLineSeriesService, LegendService, CategoryService, LineSeriesService } from '@syncfusion/ej2-angular-charts';
@Component({
imports: [ChartModule],
providers: [ CategoryService, DateTimeService, ColumnSeriesService],
standalone: true,
selector: 'app-accessible-chart',
template: `
<ejs-chart
id="chart-container"
[title]='title'
[primaryXAxis]='primaryXAxis'
[primaryYAxis]='primaryYAxis'
tabindex="0"
role="img"
[attr.aria-label]="chartAriaLabel">
<e-series-collection>
<e-series
[dataSource]='chartData'
xName='x'
yName='y'
type='Column'
name='Sales Data'>
</e-series>
</e-series-collection>
</ejs-chart>
`
})
export class AccessibleChartComponent {
public title: string = 'Monthly Sales Report';
public chartAriaLabel: string = 'Bar chart showing monthly sales data from January to December 2024';
public chartData: Object[] = [
{ x: 'Jan', y: 35 },
{ x: 'Feb', y: 28 },
{ x: 'Mar', y: 34 }
];
public primaryXAxis: Object = {
title: 'Month',
valueType: 'Category'
};
public primaryYAxis: Object = {
title: 'Sales (in thousands)',
labelFormat: '${value}K'
};
}Custom Keyboard Handlers:
import { Component, HostListener, ViewChild } from '@angular/core';
import { ChartComponent } from '@syncfusion/ej2-angular-charts';
@Component({
selector: 'app-chart',
template: `<ejs-chart #chart></ejs-chart>`
})
export class KeyboardChartComponent {
@ViewChild('chart') chart: ChartComponent;
private selectedPointIndex: number = 0;
@HostListener('document:keydown', ['$event'])
handleKeyboardEvent(event: KeyboardEvent): void {
// Alt + J to focus chart
if (event.altKey && event.key === 'j') {
event.preventDefault();
this.focusChart();
}
// Arrow keys for navigation
if (event.key === 'ArrowRight') {
event.preventDefault();
this.navigateToNextPoint();
} else if (event.key === 'ArrowLeft') {
event.preventDefault();
this.navigateToPreviousPoint();
}
// Enter/Space to select
if (event.key === 'Enter' || event.key === ' ') {
event.preventDefault();
this.selectCurrentPoint();
}
}
focusChart(): void {
let chartElement = document.getElementById('chart-container');
chartElement?.focus();
}
navigateToNextPoint(): void {
this.selectedPointIndex = (this.selectedPointIndex + 1) % this.chartData.length;
this.highlightPoint(this.selectedPointIndex);
this.announcePoint(this.selectedPointIndex);
}
navigateToPreviousPoint(): void {
this.selectedPointIndex = this.selectedPointIndex === 0
? this.chartData.length - 1
: this.selectedPointIndex - 1;
this.highlightPoint(this.selectedPointIndex);
this.announcePoint(this.selectedPointIndex);
}
highlightPoint(index: number): void {
// Visual highlight implementation
this.chart.series[0].points[index].marker.visible = true;
this.chart.refresh();
}
announcePoint(index: number): void {
let point = this.chartData[index];
let announcement = `${point.x}: ${point.y}`;
this.updateAriaLive(announcement);
}
updateAriaLive(message: string): void {
let liveRegion = document.getElementById('chart-live-region');
if (liveRegion) {
liveRegion.textContent = message;
}
}
selectCurrentPoint(): void {
let point = this.chartData[this.selectedPointIndex];
console.log('Selected point:', point);
// Trigger selection logic
}
}ARIA Live Region:
import { ChartModule } from '@syncfusion/ej2-angular-charts';
import { Component } from '@angular/core';
import { DateTimeService, StepLineSeriesService, LegendService, CategoryService, LineSeriesService } from '@syncfusion/ej2-angular-charts';
@Component({
imports: [ChartModule],
providers: [ CategoryService, DateTimeService, ColumnSeriesService],
standalone: true,
selector: 'app-accessible-chart',
template: `
<div class="chart-wrapper">
<ejs-chart #chart></ejs-chart>
<div
id="chart-live-region"
class="sr-only"
role="status"
aria-live="polite"
aria-atomic="true">
</div>
</div>
`,
styles: [`
.sr-only {
position: absolute;
left: -10000px;
width: 1px;
height: 1px;
overflow: hidden;
}
`]
})
export class AccessibleChartComponent {
}Screen Reader Support
ARIA Attributes
The chart component uses appropriate ARIA attributes:
Applied ARIA Roles:
role="img"- Chart containerrole="button"- Interactive elements (legend items)role="region"- Chart areasrole="status"- Live announcements
ARIA Attributes:
aria-label- Descriptive labelsaria-hidden- Hide decorative elementsaria-pressed- Toggle state for legend itemsaria-describedby- Additional descriptionsaria-live- Dynamic content announcements
Implementation Example:
import { ChartModule } from '@syncfusion/ej2-angular-charts';
import { Component } from '@angular/core';
import { CategoryService, DateTimeService, ColumnSeriesService, LegendService } from '@syncfusion/ej2-angular-charts';
@Component({
imports: [ChartModule],
providers: [ CategoryService, DateTimeService, ColumnSeriesService, LegendService],
standalone: true,
selector: 'app-accessible-chart',
template: `
<ejs-chart
role="img"
[attr.aria-label]="chartDescription"
[attr.aria-describedby]="'chart-details'">
<e-series-collection>
<e-series
[dataSource]='salesData'
[attr.aria-label]="'Sales data series'">
</e-series>
<e-series
[dataSource]='targetData'
[attr.aria-label]="'Target data series'">
</e-series>
</e-series-collection>
<e-legendsettings
visible='true'
[attr.aria-label]="'Chart legend'">
</e-legendsettings>
</ejs-chart>
<div id="chart-details" class="sr-only">
<p>{{ detailedDescription }}</p>
<ul>
<li *ngFor="let item of accessibilityData">
{{ item.label }}: {{ item.value }}
</li>
</ul>
</div>
`
})
export class ScreenReaderChartComponent {
public chartDescription: string = 'Column chart comparing monthly sales versus targets for 2024';
public detailedDescription: string = `
This chart displays sales performance throughout the year.
Sales are shown in blue columns, targets in red line.
Use arrow keys to navigate between data points.
Press Enter to select a data point for more details.
`;
public accessibilityData = [
{ label: 'Highest sales month', value: 'June with $85,000' },
{ label: 'Lowest sales month', value: 'February with $45,000' },
{ label: 'Average monthly sales', value: '$62,500' },
{ label: 'Target achievement', value: '94%' }
];
}Descriptive Labels
Provide meaningful descriptions for all chart elements:
export class DescriptiveChartComponent {
// Chart-level description
public getChartLabel(): string {
return `${this.title}. ${this.getChartType()} chart with ${this.seriesCount} series
showing data from ${this.startDate} to ${this.endDate}`;
}
// Series-level description
public getSeriesLabel(series: any): string {
return `${series.name} series. Type: ${series.type}.
${series.data.length} data points from ${series.minValue} to ${series.maxValue}`;
}
// Point-level description
public getPointLabel(point: any): string {
return `${point.category}: ${point.value}.
${this.getPointContext(point)}`;
}
private getPointContext(point: any): string {
if (point.value === this.maxValue) {
return 'Highest value in the series';
} else if (point.value === this.minValue) {
return 'Lowest value in the series';
}
return '';
}
}Color Contrast
WCAG AA Requirements
Ensure sufficient contrast ratios:
Minimum Contrast Ratios:
- Normal text: 4.5:1
- Large text (18pt+): 3:1
- UI components: 3:1
- Graphical objects: 3:1
Implementation:
export class HighContrastChartComponent {
// High contrast color palette
public accessiblePalette: string[] = [
'#0066CC', // Blue (contrast: 8.59:1 on white)
'#CC0000', // Red (contrast: 5.25:1 on white)
'#00AA00', // Green (contrast: 4.51:1 on white)
'#FF8800', // Orange (contrast: 4.54:1 on white)
'#6600CC' // Purple (contrast: 6.22:1 on white)
];
public chartArea: Object = {
background: '#FFFFFF', // White background
border: {
width: 2,
color: '#000000' // Black border (21:1 contrast)
}
};
// Verify contrast programmatically
verifyContrast(foreground: string, background: string): boolean {
let ratio = this.calculateContrastRatio(foreground, background);
return ratio >= 4.5; // WCAG AA standard
}
calculateContrastRatio(fg: string, bg: string): number {
let l1 = this.getLuminance(fg);
let l2 = this.getLuminance(bg);
let lighter = Math.max(l1, l2);
let darker = Math.min(l1, l2);
return (lighter + 0.05) / (darker + 0.05);
}
getLuminance(color: string): number {
// Convert color to RGB and calculate relative luminance
// Implementation details...
return 0;
}
}High Contrast Mode
Support high contrast themes:
import { ChartModule } from '@syncfusion/ej2-angular-charts';
import { Component } from '@angular/core';
import { CategoryService, DateTimeService, ColumnSeriesService } from '@syncfusion/ej2-angular-charts';
@Component({
imports: [ChartModule],
providers: [ CategoryService, DateTimeService, ColumnSeriesService],
standalone: true,
selector: 'app-accessible-chart',
template: `
<ejs-chart
[theme]='currentTheme'
[palettes]='contrastPalette'>
<!-- chart configuration -->
</ejs-chart>
`
})
export class HighContrastComponent implements OnInit {
public currentTheme: string = 'Material';
public contrastPalette: string[];
ngOnInit() {
this.detectHighContrastMode();
}
detectHighContrastMode(): void {
// Check Windows high contrast mode
if (window.matchMedia('(prefers-contrast: high)').matches) {
this.enableHighContrast();
}
// Listen for changes
window.matchMedia('(prefers-contrast: high)').addEventListener('change', (e) => {
if (e.matches) {
this.enableHighContrast();
} else {
this.disableHighContrast();
}
});
}
enableHighContrast(): void {
this.currentTheme = 'HighContrast';
this.contrastPalette = [
'#FFFFFF', // White
'#FFFF00', // Yellow
'#00FFFF', // Cyan
'#FF00FF', // Magenta
'#00FF00' // Lime
];
}
disableHighContrast(): void {
this.currentTheme = 'Material';
this.contrastPalette = this.defaultPalette;
}
}CSS for High Contrast:
/* High contrast mode styles */
@media (prefers-contrast: high) {
.e-chart {
border: 2px solid currentColor;
}
.e-chart .e-series-0 {
fill: #FFFFFF;
stroke: #000000;
stroke-width: 2;
}
.e-chart .e-axis-line,
.e-chart .e-grid-line {
stroke: #FFFFFF;
stroke-width: 2;
}
.e-chart .e-chart-title,
.e-chart .e-axis-label {
fill: #FFFFFF;
font-weight: bold;
}
}Internationalization
Number Formatting
Format numbers according to locale:
import { ChartModule } from '@syncfusion/ej2-angular-charts';
import { Component } from '@angular/core';
import { CategoryService, DateTimeService, ColumnSeriesService, LegendService } from '@syncfusion/ej2-angular-charts';
import { loadCldr, L10n, setCulture } from '@syncfusion/ej2-base';
// Import CLDR data
import * as numberingSystems from 'cldr-data/supplemental/numberingSystems.json';
import * as gregorian from 'cldr-data/main/de/ca-gregorian.json';
import * as numbers from 'cldr-data/main/de/numbers.json';
import * as timeZoneNames from 'cldr-data/main/de/timeZoneNames.json';
loadCldr(numberingSystems, gregorian, numbers, timeZoneNames);
@Component({
imports: [ChartModule],
providers: [ CategoryService, DateTimeService, ColumnSeriesService],
standalone: true,
selector: 'app-intl-chart',
template: `
<select (change)="changeLocale($event.target.value)">
<option value="en-US">English (US)</option>
<option value="de-DE">German</option>
<option value="fr-FR">French</option>
<option value="ja-JP">Japanese</option>
</select>
<ejs-chart [locale]='locale'>
<e-primaryyaxis [labelFormat]='labelFormat'></e-primaryyaxis>
<e-series-collection>
<e-series [dataSource]='data' [marker]='marker'></e-series>
</e-series-collection>
</ejs-chart>
`
})
export class IntlChartComponent {
public locale: string = 'en-US';
public labelFormat: string = 'n2'; // Number format with 2 decimals
public marker: Object = {
dataLabel: {
visible: true,
format: 'n0' // Number format without decimals
}
};
changeLocale(locale: string): void {
this.locale = locale;
setCulture(locale);
// Update format based on locale
if (locale === 'de-DE') {
this.labelFormat = 'n2'; // Uses German number format (1.234,56)
} else if (locale === 'fr-FR') {
this.labelFormat = 'n2'; // Uses French number format (1 234,56)
}
}
}Date Formatting
export class DateIntlChartComponent {
public primaryXAxis: Object = {
valueType: 'DateTime',
labelFormat: 'MMM yyyy', // Will format according to locale
skeleton: 'yMd' // Use skeleton for locale-aware formatting
};
public locale: string = 'en-US';
changeLocale(locale: string): void {
this.locale = locale;
setCulture(locale);
// Date format automatically adapts to locale
// en-US: 1/15/2024
// de-DE: 15.1.2024
// ja-JP: 2024/1/15
}
}Currency Display
export class CurrencyChartComponent {
public locale: string = 'en-US';
public currency: string = 'USD';
public primaryYAxis: Object = {
labelFormat: 'c2' // Currency format with 2 decimals
};
public marker: Object = {
dataLabel: {
visible: true,
format: 'c0' // Currency without decimals
}
};
changeRegion(locale: string, currency: string): void {
this.locale = locale;
this.currency = currency;
setCulture(locale);
// Currency symbols adapt to locale:
// en-US, USD: $1,234.56
// de-DE, EUR: 1.234,56 €
// ja-JP, JPY: ¥1,235
}
}Localization
RTL Support
Support right-to-left languages:
import { ChartModule } from '@syncfusion/ej2-angular-charts';
import { Component } from '@angular/core';
import { CategoryService, DateTimeService, ColumnSeriesService, LegendService } from '@syncfusion/ej2-angular-charts';
import { enableRtl } from '@syncfusion/ej2-base';
@Component({
imports: [ChartModule],
providers: [ CategoryService, DateTimeService, ColumnSeriesService],
standalone: true,
selector: 'app-rtl-chart',
template: `
<div [dir]="direction">
<ejs-chart
[enableRtl]='isRtl'
[title]='title'>
<e-series-collection>
<e-series [dataSource]='data'></e-series>
</e-series-collection>
</ejs-chart>
</div>
`
})
export class RtlChartComponent {
public isRtl: boolean = false;
public direction: string = 'ltr';
public title: string = 'Sales Report';
enableRtlMode(): void {
this.isRtl = true;
this.direction = 'rtl';
enableRtl(true);
this.title = 'تقرير المبيعات'; // Arabic title
}
disableRtlMode(): void {
this.isRtl = false;
this.direction = 'ltr';
enableRtl(false);
this.title = 'Sales Report';
}
}Translating Text Content
import { ChartModule } from '@syncfusion/ej2-angular-charts';
import { Component } from '@angular/core';
import { CategoryService, DateTimeService, ColumnSeriesService, LegendService } from '@syncfusion/ej2-angular-charts';
import { L10n } from '@syncfusion/ej2-base';
// Load German translations
L10n.load({
'de': {
'chart': {
'Zoom': 'Zoomen',
'ZoomIn': 'Hineinzoomen',
'ZoomOut': 'Herauszoomen',
'Reset': 'Zurücksetzen',
'Pan': 'Schwenken',
'ResetZoom': 'Zoom zurücksetzen'
}
}
});
// Load French translations
L10n.load({
'fr': {
'chart': {
'Zoom': 'Zoom',
'ZoomIn': 'Zoom avant',
'ZoomOut': 'Zoom arrière',
'Reset': 'Réinitialiser',
'Pan': 'Panoramique',
'ResetZoom': 'Réinitialiser le zoom'
}
}
});
@Component({
imports: [ChartModule],
providers: [ CategoryService, DateTimeService, ColumnSeriesService],
standalone: true,
selector: 'app-accessible-chart',
template: `
<ejs-chart [locale]='locale' [zoomSettings]='zoomSettings'>
</ejs-chart>
`
})
export class LocalizedChartComponent {
public locale: string = 'de';
public zoomSettings: Object = {
enableSelectionZooming: true,
enablePan: true,
showToolbar: true // Toolbar buttons will use localized text
};
}Testing Accessibility
Automated Testing
import { TestBed } from '@angular/core/testing';
import { axe, toHaveNoViolations } from 'jest-axe';
describe('ChartComponent Accessibility', () => {
beforeEach(() => {
jasmine.addMatchers(toHaveNoViolations);
});
it('should have no accessibility violations', async () => {
const fixture = TestBed.createComponent(ChartComponent);
fixture.detectChanges();
const results = await axe(fixture.nativeElement);
expect(results).toHaveNoViolations();
});
it('should have proper ARIA labels', () => {
const fixture = TestBed.createComponent(ChartComponent);
const chartElement = fixture.nativeElement.querySelector('.e-chart');
expect(chartElement.getAttribute('role')).toBe('img');
expect(chartElement.getAttribute('aria-label')).toBeTruthy();
});
it('should be keyboard navigable', () => {
const fixture = TestBed.createComponent(ChartComponent);
const chartElement = fixture.nativeElement.querySelector('.e-chart');
expect(chartElement.getAttribute('tabindex')).toBe('0');
});
});Manual Testing Checklist
- [ ] Navigate chart using keyboard only
- [ ] Verify screen reader announces all data
- [ ] Test with high contrast mode
- [ ] Check color contrast ratios
- [ ] Verify focus indicators visible
- [ ] Test RTL layout if applicable
- [ ] Verify zoom/pan keyboard controls
- [ ] Test with different locales
- [ ] Check tooltip accessibility
- [ ] Verify legend keyboard interaction
Best Practices
1. Always Provide Text Alternatives:
<ejs-chart
[attr.aria-label]="comprehensiveDescription"
[attr.aria-describedby]="'data-table'">
</ejs-chart>
<table id="data-table" class="sr-only">
<!-- Tabular representation of chart data -->
</table>2. Use Semantic HTML:
<section aria-labelledby="chart-heading">
<h2 id="chart-heading">Sales Analysis</h2>
<ejs-chart></ejs-chart>
</section>3. Ensure Focus Management:
manageFocus(): void {
// Save focus before modal/overlay
this.previousFocus = document.activeElement;
// Restore focus after closing
setTimeout(() => {
this.previousFocus?.focus();
}, 0);
}4. Provide Data Table Alternative:
@Component({
template: `
<div class="chart-container">
<ejs-chart [dataSource]='chartData'></ejs-chart>
<button (click)="toggleDataTable()">
{{ showTable ? 'Hide' : 'Show' }} Data Table
</button>
<table *ngIf="showTable" class="data-table">
<caption>Chart Data in Table Format</caption>
<thead>
<tr>
<th>Category</th>
<th>Value</th>
</tr>
</thead>
<tbody>
<tr *ngFor="let item of chartData">
<td>{{ item.x }}</td>
<td>{{ item.y }}</td>
</tr>
</tbody>
</table>
</div>
`
})5. Test with Real Users:
- Include users with disabilities in testing
- Test with actual assistive technologies
- Gather feedback and iterate
Accessibility Patterns
Pattern 1: Progressive Disclosure
export class ProgressiveDisclosureChart {
public summaryText: string = 'Sales increased by 15% in Q4';
public showDetails: boolean = false;
@Component({
template: `
<div class="chart-summary">
<p>{{ summaryText }}</p>
<button (click)="showDetails = !showDetails">
{{ showDetails ? 'Hide' : 'Show' }} Detailed Chart
</button>
</div>
<ejs-chart *ngIf="showDetails"
[attr.aria-label]="detailedDescription">
</ejs-chart>
`
})
}Pattern 2: Sonification (Audio Charts)
export class SonificationChart {
playDataPoint(point: any): void {
let audioContext = new AudioContext();
let oscillator = audioContext.createOscillator();
// Map data value to frequency
let frequency = this.mapValueToFrequency(point.y);
oscillator.frequency.value = frequency;
oscillator.connect(audioContext.destination);
oscillator.start();
oscillator.stop(audioContext.currentTime + 0.3);
}
mapValueToFrequency(value: number): number {
// Map value range to frequency range (200-800 Hz)
let minFreq = 200;
let maxFreq = 800;
let normalized = (value - this.minValue) / (this.maxValue - this.minValue);
return minFreq + (normalized * (maxFreq - minFreq));
}
}Troubleshooting
Screen Reader Not Announcing Data
Issue: Screen reader skips chart content Solutions:
- Add
role="img"to chart container - Provide comprehensive
aria-label - Ensure chart is in DOM (not hidden)
- Check
aria-hiddennot set to true - Add
tabindex="0"for focus
Keyboard Navigation Not Working
Issue: Cannot navigate with keyboard Solutions:
- Verify
tabindex="0"on chart - Check for event handler conflicts
- Ensure focus styles visible
- Test without mouse
- Check z-index layering
Low Color Contrast
Issue: Failing contrast checks Solutions:
- Use contrast checker tool
- Apply high contrast palette
- Increase border widths
- Use patterns in addition to colors
- Test with high contrast mode
---
Advanced Features Reference for Syncfusion Angular Chart
Table of Contents
- Introduction
- Chart Events
- Lifecycle Events
- Interaction Events
- Render Events
- Animation Events
- Export Functionality
- Image Export
- PDF Export
- Excel Export
- Base64 Export
- Print Functionality
- Render Methods
- SVG Rendering
- Canvas Rendering
- Module Injection
- Animation Control
- Selection and Highlighting
- Zooming and Panning
- Crosshair and Trackball
- Synchronized Charts
- Best Practices
- Advanced Patterns
- Troubleshooting
Introduction
The Syncfusion Angular Chart component offers advanced features that extend beyond basic visualization. These features include comprehensive event handling, export capabilities, print support, and interactive elements that enhance user experience and data analysis.
This reference covers all advanced features with practical examples and implementation guidance.
Chart Events
Lifecycle Events
load Event: Triggered before the chart begins rendering. Use this to configure properties programmatically.
import { ChartModule } from '@syncfusion/ej2-angular-charts'
import { CategoryService, BarSeriesService, ColumnSeriesService, LineSeriesService, LegendService, DataLabelService, MultiLevelLabelService, SelectionService } from '@syncfusion/ej2-angular-charts'
import { ILoadedEventArgs } from '@syncfusion/ej2-charts';
@Component({
imports: [ ChartModule ],
providers: [ CategoryService, BarSeriesService, ColumnSeriesService, LineSeriesService,LegendService, DataLabelService, MultiLevelLabelService, SelectionService],
standalone: true,
selector: 'app-chart',
template: `
<ejs-chart id="chart-container" (load)='onLoad($event)' [primaryXAxis]='primaryXAxis'[primaryYAxis]='primaryYAxis' [title]='title'>
<e-series-collection>
<e-series [dataSource]='chartData' type='Column' xName='country' yName='gold' name='Gold'></e-series>
</e-series-collection>
</ejs-chart>
`
})
export class ChartComponent {
public primaryXAxis?: Object;
public chartData?: Object[];
public title?: string;
public primaryYAxis?: Object;
onLoad(args: ILoadedEventArgs): void {
// Set theme based on user preference
let savedTheme = localStorage.getItem('chartTheme');
if (savedTheme) {
args.chart.theme = savedTheme;
}
// Apply custom palettes
args.chart.palettes = this.getCustomPalette();
// Configure locale
args.chart.locale = this.getCurrentLocale();
};
ngOnInit(): void {
this.chartData = [
{ country: "USA", gold: 50 },
{ country: "China", gold: 40 },
{ country: "Japan", gold: 70 },
{ country: "Australia", gold: 60 },
{ country: "France", gold: 50 },
{ country: "Germany", gold: 40 },
{ country: "Italy", gold: 40 },
{ country: "Sweden", gold: 30, silver: 25 }
];
this.primaryXAxis = {
valueType: 'Category',
title: 'Countries'
};
this.primaryYAxis = {
minimum: 0, maximum: 80,
interval: 20, title: 'Medals'
};
this.title = 'Olympic Medals';
}
}loaded Event: Triggered after chart rendering completes.
onLoaded(args: ILoadedEventArgs): void {
console.log('Chart loaded successfully');
// Perform post-render operations
this.hideLoadingSpinner();
this.enableInteractions();
this.trackAnalytics('chart_viewed');
// Access rendered elements
let svg = args.chart.svgObject;
// Custom SVG manipulations if needed
}beforeResize Event: Triggered before chart resize. Can cancel the resize.
import { IBeforeResizeEventArgs } from '@syncfusion/ej2-charts';
onBeforeResize(args: IBeforeResizeEventArgs): void {
// Cancel resize if dimensions are too small
if (args.currentSize.width < 300 || args.currentSize.height < 200) {
args.cancel = true;
this.showResizeWarning();
}
// Log resize event
console.log('Resizing from', args.previousSize, 'to', args.currentSize);
}resized Event: Triggered after chart is resized.
import { IResizeEventArgs } from '@syncfusion/ej2-charts';
onResized(args: IResizeEventArgs): void {
console.log('Chart resized to:', args.currentSize);
// Adjust elements based on new size
if (args.currentSize.width < 500) {
this.switchToMobileLayout();
} else {
this.switchToDesktopLayout();
}
}Interaction Events
pointClick Event: Triggered when a data point is clicked.
import { IPointEventArgs } from '@syncfusion/ej2-charts';
onPointClick(args: IPointEventArgs): void {
console.log('Point clicked:', args.point);
// Show detailed information
this.showPointDetails({
series: args.seriesIndex,
point: args.pointIndex,
x: args.point.x,
y: args.point.y
});
// Drill down to details
if (args.point.x === 'Q1') {
this.loadQuarterlyDetails('Q1');
}
}pointDoubleClick Event: Triggered on double-clicking a data point.
onPointDoubleClick(args: IPointEventArgs): void {
// Open detailed view
this.openDetailModal(args.point);
// Or zoom to point
this.zoomToPoint(args.point);
}pointMove Event: Triggered when hovering over a data point.
onPointMove(args: IPointEventArgs): void {
// Update external indicator
this.updateSelectionIndicator(args.point);
// Show preview
this.showQuickPreview(args.point);
}chartMouseClick Event: Triggered when clicking anywhere on the chart.
import { IMouseEventArgs } from '@syncfusion/ej2-charts';
onChartMouseClick(args: IMouseEventArgs): void {
console.log('Clicked at:', args.x, args.y);
// Get axis values at click position
if (args.target.includes('Series')) {
let xValue = args.axisData.primaryXAxis;
let yValue = args.axisData.primaryYAxis;
console.log('Axis values:', xValue, yValue);
}
// Clear selection if clicking empty area
if (!args.target.includes('Point')) {
this.clearSelection();
}
}legendClick Event: Triggered when clicking a legend item.
import { ILegendClickEventArgs } from '@syncfusion/ej2-charts';
onLegendClick(args: ILegendClickEventArgs): void {
console.log('Legend clicked:', args.series.name);
// Custom toggle behavior
if (args.series.name === 'Critical Data') {
args.cancel = true; // Prevent default toggle
this.showWarning('This series cannot be hidden');
}
// Track visibility
this.trackSeriesVisibility(args.series.name, !args.series.visible);
}Render Events
seriesRender Event: Triggered before a series renders.
import { ISeriesRenderEventArgs } from '@syncfusion/ej2-charts';
onSeriesRender(args: ISeriesRenderEventArgs): void {
// Conditional styling
if (args.series.name === 'Actual') {
args.fill = '#4CAF50';
} else if (args.series.name === 'Forecast') {
args.fill = '#FF9800';
args.dashArray = '5,5'; // Dashed line for forecast
}
// Hide series based on condition
if (this.hideProjections && args.series.name.includes('Projection')) {
args.cancel = true;
}
}pointRender Event: Triggered before each point renders.
import { IPointRenderEventArgs } from '@syncfusion/ej2-charts';
onPointRender(args: IPointRenderEventArgs): void {
// Color-code by value
if (args.point.y > 80) {
args.fill = '#4CAF50'; // Green for high
} else if (args.point.y > 50) {
args.fill = '#FFC107'; // Yellow for medium
} else {
args.fill = '#F44336'; // Red for low
}
// Highlight specific points
if (args.point.x === this.selectedMonth) {
args.border = { width: 3, color: '#000000' };
}
// Custom shapes
if (args.point.y === this.maxValue) {
args.shape = 'Diamond';
}
}textRender Event: Triggered before data labels render.
import { ITextRenderEventArgs } from '@syncfusion/ej2-charts';
onTextRender(args: ITextRenderEventArgs): void {
// Format label text
args.text = '$' + parseFloat(args.text).toLocaleString();
// Conditional styling
if (parseFloat(args.text) < 0) {
args.color = '#FF0000';
args.text = '(' + Math.abs(parseFloat(args.text)) + ')';
}
// Hide labels for small values
if (parseFloat(args.text) < 10) {
args.text = '';
}
}axisLabelRender Event: Triggered before axis labels render.
import { IAxisLabelRenderEventArgs } from '@syncfusion/ej2-charts';
onAxisLabelRender(args: IAxisLabelRenderEventArgs): void {
// Abbreviate large numbers
if (args.axis.name === 'primaryYAxis') {
if (args.value >= 1000000) {
args.text = (args.value / 1000000).toFixed(1) + 'M';
} else if (args.value >= 1000) {
args.text = (args.value / 1000).toFixed(0) + 'K';
}
}
// Custom date formatting
if (args.axis.valueType === 'DateTime') {
let date = new Date(args.value);
args.text = date.toLocaleDateString('en-US', { month: 'short' });
}
}tooltipRender Event: Triggered before tooltip displays.
import { ITooltipRenderEventArgs } from '@syncfusion/ej2-charts';
onTooltipRender(args: ITooltipRenderEventArgs): void {
// Custom tooltip content
args.text = `
<div style="padding: 10px;">
<strong>${args.point.x}</strong><br/>
Sales: $${args.point.y.toLocaleString()}<br/>
Growth: ${this.calculateGrowth(args.point)}%
</div>
`;
// Custom styling
args.headerText = '<strong>Sales Data</strong>';
args.textStyle.color = '#FFFFFF';
}Animation Events
animationComplete Event: Triggered when series animation completes.
import { IAnimationCompleteEventArgs } from '@syncfusion/ej2-charts';
onAnimationComplete(args: IAnimationCompleteEventArgs): void {
console.log('Animation completed for series:', args.series.name);
// Enable interactions after animation
this.enableUserInteractions();
// Start next animation
if (this.animationQueue.length > 0) {
this.startNextAnimation();
}
}Export Functionality
Image Export
Export chart as PNG, JPEG, or SVG:
import { Component, ViewChild } from '@angular/core';
import { ChartComponent, ExportType } from '@syncfusion/ej2-angular-charts';
import { data } from './datasource';
@Component({
imports: [ChartModule],
providers: [ CategoryService, DateTimeService, ColumnSeriesService],
standalone: true,
selector: 'app-chart',
template: `
<button (click)="exportChart('PNG')">Export PNG</button>
<button (click)="exportChart('JPEG')">Export JPEG</button>
<button (click)="exportChart('SVG')">Export SVG</button>
<ejs-chart #chart
[title]='title'
(beforeExport)='onBeforeExport($event)'>
<e-series-collection>
<e-series [dataSource]='data'></e-series>
</e-series-collection>
</ejs-chart>
`
})
export class ExportChartComponent {
@ViewChild('chart') chart: ChartComponent;
exportChart(type: ExportType): void {
this.chart.export(type, 'ChartExport');
}
onBeforeExport(args: IExportEventArgs): void {
// Customize export
args.width = 1920;
args.height = 1080;
}
}Custom Export Settings:
exportWithSettings(): void {
this.chart.export('PNG', 'SalesChart', null, [this.chart], 1920, 1080);
// type fileName orientation controls width height
}PDF Export
Export chart to PDF with custom settings:
exportToPDF(): void {
this.chart.export('PDF', 'ChartReport', 'Portrait');
}
exportWithHeaderFooter(): void {
this.chart.export(
'PDF',
'MonthlyReport',
'Landscape',
[this.chart],
null,
null,
true, // allowDownload
{
header: {
fromTop: 0,
height: 130,
contents: [
{
type: 'Text',
value: 'Sales Performance Report',
position: { x: 250, y: 50 },
style: { fontSize: 20, bold: true }
}
]
},
footer: {
fromBottom: 160,
height: 150,
contents: [
{
type: 'PageNumber',
format: 'Page {$current} of {$total}',
position: { x: 250, y: 50 },
style: { fontSize: 12 }
},
{
type: 'Text',
value: '© 2024 Company Name',
position: { x: 250, y: 80 }
}
]
}
}
);
}Multiple Charts in PDF:
@ViewChild('chart1') chart1: ChartComponent;
@ViewChild('chart2') chart2: ChartComponent;
exportMultipleCharts(): void {
this.chart1.export(
'PDF',
'CombinedReport',
'Portrait',
[this.chart1, this.chart2], // Multiple chart instances
null,
null,
true,
null,
false // exportToMultiplePage: false = same page
);
}
exportToSeparatePages(): void {
this.chart1.export(
'PDF',
'DetailedReport',
'Portrait',
[this.chart1, this.chart2],
null,
null,
true,
null,
true // exportToMultiplePage: true = separate pages
);
}Excel Export
Export chart data to Excel:
import { IExportEventArgs } from '@syncfusion/ej2-charts';
import { ChartModule, ChartComponent } from '@syncfusion/ej2-angular-charts';
import { Component } from '@angular/core';
import { CategoryService, DateTimeService, ColumnSeriesService, LegendService } from '@syncfusion/ej2-angular-charts';
@Component({
imports: [ChartModule],
providers: [ CategoryService, DateTimeService, ColumnSeriesService],
standalone: true,
selector: 'app-chart',
template: `
<button (click)="exportToExcel('XLSX')">Export to Excel</button>
<button (click)="exportToExcel('CSV')">Export to CSV</button>
<ejs-chart #chart (beforeExport)='onBeforeExport($event)'>
</ejs-chart>
`
})
export class ExcelExportComponent {
@ViewChild('chart') chart: ChartComponent;
exportToExcel(type: 'XLSX' | 'CSV'): void {
this.chart.export(type, 'ChartData');
}
onBeforeExport(args: IExportEventArgs): void {
if (args.type === 'XLSX' || args.type === 'CSV') {
// Customize Excel export
args.excelProperties = {
header: {
headerRows: 2,
rows: [
{
cells: [
{
colSpan: 4,
value: 'Sales Report 2024',
style: { fontSize: 20, bold: true }
}
]
},
{
cells: [
{ value: 'Generated on: ' + new Date().toLocaleDateString() }
]
}
]
},
footer: {
footerRows: 1,
rows: [
{
cells: [
{ value: '© 2024 Company Name' }
]
}
]
}
};
}
}
}Custom Excel Formatting:
onBeforeExport(args: IExportEventArgs): void {
if (args.type === 'XLSX') {
args.excelProperties = {
dataSource: this.customDataFormat(),
columns: [
{ field: 'month', headerText: 'Month', width: 120 },
{ field: 'sales', headerText: 'Sales ($)', width: 150 },
{ field: 'growth', headerText: 'Growth (%)', width: 120 }
],
header: {
headerRows: 3,
rows: [
{ cells: [{ value: 'Annual Report', style: { fontSize: 24, bold: true } }] },
{ cells: [{ value: 'Fiscal Year 2024' }] },
{ cells: [{ value: '' }] } // Empty row for spacing
]
}
};
}
}
customDataFormat(): any[] {
return this.chartData.map(item => ({
month: item.x,
sales: item.y,
growth: this.calculateGrowth(item)
}));
}Base64 Export
Export chart as base64 string for embedding or transmission:
import { Component, ViewChild } from '@angular/core';
import { ChartModule, ChartComponent } from '@syncfusion/ej2-angular-charts';
import { Component } from '@angular/core';
import { CategoryService, DateTimeService, ColumnSeriesService, LegendService } from '@syncfusion/ej2-angular-charts';
@Component({
imports: [ChartModule],
providers: [ CategoryService, DateTimeService, ColumnSeriesService],
standalone: true,
selector: 'app-chart',
template: `
<button (click)="getBase64()">Get Base64</button>
<img [src]="base64Image" *ngIf="base64Image" />
<ejs-chart #chart id="chartContainer">
</ejs-chart>
`
})
export class Base64ExportComponent {
@ViewChild('chart') chart: ChartComponent;
public base64Image: string;
getBase64(): void {
let chartElement = document.getElementById('chartContainer');
let canvas = document.createElement('canvas');
let svg = chartElement.querySelector('svg');
if (svg) {
let serializer = new XMLSerializer();
let svgString = serializer.serializeToString(svg);
canvas.width = svg.clientWidth;
canvas.height = svg.clientHeight;
let ctx = canvas.getContext('2d');
let img = new Image();
img.onload = () => {
ctx.drawImage(img, 0, 0);
this.base64Image = canvas.toDataURL('image/png');
console.log('Base64:', this.base64Image);
// Can now send to server or embed
this.sendToServer(this.base64Image);
};
img.src = 'data:image/svg+xml;base64,' + btoa(svgString);
}
}
sendToServer(base64: string): void {
this.http.post('/api/save-chart', { image: base64 }).subscribe();
}
}Print Functionality
Print charts directly from the browser:
import { ChartModule } from '@syncfusion/ej2-angular-charts';
import { Component } from '@angular/core';
import { CategoryService, ChartComponent, DateTimeService, ColumnSeriesService, LegendService } from '@syncfusion/ej2-angular-charts';
@Component({
imports: [ChartModule],
providers: [ CategoryService, DateTimeService, ColumnSeriesService],
standalone: true,
selector: 'app-chart',
template: `
<button (click)="printChart()">Print Chart</button>
<button (click)="printMultiple()">Print All Charts</button>
<ejs-chart #chart1 id="chart1" (beforePrint)='onBeforePrint($event)'>
</ejs-chart>
<ejs-chart #chart2 id="chart2">
</ejs-chart>
`
})
export class PrintChartComponent {
@ViewChild('chart1') chart1: ChartComponent;
@ViewChild('chart2') chart2: ChartComponent;
printChart(): void {
this.chart1.print();
}
printMultiple(): void {
this.chart1.print(['chart1', 'chart2']);
}
onBeforePrint(args: IPrintEventArgs): void {
// Customize print content
console.log('Printing chart...');
// Add custom header
let header = document.createElement('div');
header.innerHTML = '<h1>Sales Report</h1><p>Printed on: ' + new Date().toLocaleDateString() + '</p>';
args.htmlContent.prepend(header);
}
}Print with Custom Styling:
printWithStyles(): void {
// Inject print styles
let style = document.createElement('style');
style.innerHTML = `
@media print {
@page {
size: landscape;
margin: 2cm;
}
.e-chart {
page-break-inside: avoid;
}
.no-print {
display: none !important;
}
}
`;
document.head.appendChild(style);
this.chart.print();
// Clean up
setTimeout(() => {
document.head.removeChild(style);
}, 1000);
}Render Methods
SVG Rendering
SVG is the default rendering mode. Best for:
- Accessibility (DOM-based)
- Crisp vector output
- Small to moderate datasets
- Interactive features
<ejs-chart>
<!-- SVG rendering (default) -->
</ejs-chart>SVG Advantages:
- Scalable without quality loss
- CSS and animation support
- Easy DOM manipulation
- Better accessibility
Canvas Rendering
Enable canvas for better performance with large datasets:
import { ChartModule } from '@syncfusion/ej2-angular-charts';
import { Component } from '@angular/core';
import { CategoryService, DateTimeService, ColumnSeriesService, LegendService } from '@syncfusion/ej2-angular-charts';
import { largeDataset } from './datasource';
@Component({
imports: [ChartModule],
providers: [ CategoryService, DateTimeService, ColumnSeriesService],
standalone: true,
selector: 'app-chart',
template: `
<ejs-chart [enableCanvas]='true'>
<e-series-collection>
<e-series [dataSource]='largeDataset'></e-series>
</e-series-collection>
</ejs-chart>
`
})
export class CanvasChartComponent {
public largeDataset: Object[] = this.generateLargeDataset(50000);
generateLargeDataset(count: number): Object[] {
let data = [];
for (let i = 0; i < count; i++) {
data.push({ x: i, y: Math.random() * 100 });
}
return data;
}
}When to Use Canvas:
- Datasets > 5,000 points
- High-frequency updates
- Limited device resources
- Real-time data streaming
Canvas Limitations:
- No native tooltips (requires custom implementation)
- Limited animation support
- Lower accessibility
- Cannot export as SVG
Performance Comparison:
export class RenderComparisonComponent {
testPerformance(): void {
// Test SVG
let svgStart = performance.now();
this.renderSVGChart(this.largeData);
let svgTime = performance.now() - svgStart;
// Test Canvas
let canvasStart = performance.now();
this.renderCanvasChart(this.largeData);
let canvasTime = performance.now() - canvasStart;
console.log('SVG render time:', svgTime, 'ms');
console.log('Canvas render time:', canvasTime, 'ms');
console.log('Performance gain:', ((svgTime - canvasTime) / svgTime * 100).toFixed(2), '%');
}
}Module Injection
Inject feature modules as needed to reduce bundle size:
import { Component } from '@angular/core';
import {
ChartModule,
LineSeriesService,
ColumnSeriesService,
CategoryService,
LegendService,
TooltipService,
DataLabelService,
ZoomService,
ScrollBarService,
DateTimeService
} from '@syncfusion/ej2-angular-charts';
@Component({
selector: 'app-chart',
standalone: true,
imports: [ChartModule],
providers: [
LineSeriesService,
ColumnSeriesService,
CategoryService,
LegendService,
TooltipService,
DataLabelService,
ZoomService,
ScrollBarService,
DateTimeService
],
template: `<ejs-chart></ejs-chart>`
})
export class ChartComponent {}Available Services:
| Service | Feature |
|---|---|
| LineSeriesService | Line chart |
| ColumnSeriesService | Column chart |
| AreaSeriesService | Area chart |
| BarSeriesService | Bar chart |
| StackingColumnSeriesService | Stacked column |
| StackingAreaSeriesService | Stacked area |
| ScatterSeriesService | Scatter chart |
| SplineSeriesService | Spline chart |
| CategoryService | Category axis |
| DateTimeService | DateTime axis |
| LogarithmicService | Logarithmic axis |
| LegendService | Legend |
| TooltipService | Tooltip |
| DataLabelService | Data labels |
| ZoomService | Zooming |
| ScrollBarService | Scrollbar |
| SelectionService | Selection |
| CrosshairService | Crosshair |
| ExportService | Export functionality |
Animation Control
Fine-tune chart animations:
export class AnimationComponent {
// Global animation settings
public animation: Object = {
enable: true,
duration: 1500,
delay: 100
};
// Per-series animation
public series1Animation: Object = {
enable: true,
duration: 1000,
delay: 0
};
public series2Animation: Object = {
enable: true,
duration: 1200,
delay: 400 // Staggered animation
};
// Disable for performance
disableAnimation(): void {
this.animation = { enable: false };
this.chart.refresh();
}
// Custom animation sequence
animateSequence(): void {
this.chart.series.forEach((series, index) => {
setTimeout(() => {
series.animation.enable = true;
series.animation.duration = 800;
this.chart.refresh();
}, index * 300);
});
}
}Selection and Highlighting
Enable data point and series selection:
import { ChartModule } from '@syncfusion/ej2-angular-charts';
import { Component } from '@angular/core';
import { CategoryService, DateTimeService, ColumnSeriesService, LegendService, SelectionService } from '@syncfusion/ej2-angular-charts';
import { ISelectionCompleteEventArgs } from '@syncfusion/ej2-charts';
@Component({
imports: [ChartModule],
providers: [ CategoryService, DateTimeService, ColumnSeriesService, LegendService, SelectionService],
standalone: true,
selector: 'app-chart',
template: `
<ejs-chart
[selectionMode]='selectionMode'
(selectionComplete)='onSelectionComplete($event)'>
<e-series-collection>
<e-series
[dataSource]='data'
[selectionStyle]='selectionStyle'>
</e-series>
</e-series-collection>
</ejs-chart>
`
})
export class SelectionComponent {
public selectionMode: string = 'Point'; // Point, Series, Cluster, DragXY, DragX, DragY
public selectionStyle: string = 'chartSelection1'; // CSS class
onSelectionComplete(args: ISelectionCompleteEventArgs): void {
console.log('Selected:', args.selectedDataValues);
// Process selected data
this.processSelection(args.selectedDataValues);
}
processSelection(selectedData: any[]): void {
// Calculate statistics
let total = selectedData.reduce((sum, item) => sum + item.y, 0);
let average = total / selectedData.length;
console.log('Selection total:', total);
console.log('Selection average:', average);
}
}Zooming and Panning
Enable interactive zooming and panning:
export class ZoomChartComponent {
public zoomSettings: Object = {
enableSelectionZooming: true,
enablePinchZooming: true,
enableMouseWheelZooming: true,
enableDeferredZooming: false,
enableScrollbar: true,
enablePan: true,
mode: 'XY', // X, Y, or XY
showToolbar: true,
toolbarItems: ['Zoom', 'ZoomIn', 'ZoomOut', 'Pan', 'Reset']
};
// Programmatic zoom
zoomToRange(): void {
this.chart.zoomModule.zoomByRange(
this.chart.primaryXAxis,
new Date(2024, 0, 1), // Start
new Date(2024, 5, 30) // End
);
}
// Reset zoom
resetZoom(): void {
this.chart.zoomModule.reset();
}
}Crosshair and Trackball
Display crosshair or trackball for precise data reading:
export class CrosshairComponent {
public crosshair: Object = {
enable: true,
line: {
width: 2,
color: '#FF0000'
},
lineType: 'Vertical' // Both, Horizontal, Vertical
};
public tooltip: Object = {
enable: true,
shared: true // Show data from all series
};
}Synchronized Charts
Synchronize interactions across multiple charts:
import { ChartModule } from '@syncfusion/ej2-angular-charts';
import { Component } from '@angular/core';
import { CategoryService, DateTimeService, ChartComponent, ColumnSeriesService, LegendService } from '@syncfusion/ej2-angular-charts';
import { IMouseEventArgs } from '@syncfusion/ej2-charts';
@Component({
imports: [ChartModule],
providers: [ CategoryService, DateTimeService, ColumnSeriesService],
standalone: true,
selector: 'app-chart',
template: `
<ejs-chart #chart1
(chartMouseMove)='synchronizeCharts($event)'
(chartMouseLeave)='hideTooltips()'>
</ejs-chart>
<ejs-chart #chart2>
</ejs-chart>
`
})
export class SynchronizedChartsComponent {
@ViewChild('chart1') chart1: ChartComponent;
@ViewChild('chart2') chart2: ChartComponent;
synchronizeCharts(args: IMouseEventArgs): void {
if (args.target.includes('Series')) {
let xValue = args.axisData.primaryXAxis;
// Show tooltips on both charts
this.showTooltipAtX(this.chart2, xValue);
}
}
showTooltipAtX(chart: ChartComponent, xValue: any): void {
// Implementation to show tooltip
}
hideTooltips(): void {
this.chart2.hideTooltip();
}
}Best Practices
1. Event Handler Performance:
// Debounce frequent events
import { Subject } from 'rxjs';
import { debounceTime } from 'rxjs/operators';
private mouseMoveSubject = new Subject<IMouseEventArgs>();
ngOnInit() {
this.mouseMoveSubject.pipe(
debounceTime(100)
).subscribe(args => {
this.handleMouseMove(args);
});
}
onChartMouseMove(args: IMouseEventArgs): void {
this.mouseMoveSubject.next(args);
}2. Efficient Export:
// Cache export settings
private exportConfig = {
type: 'PNG',
width: 1920,
height: 1080
};
exportOptimized(): void {
this.chart.export(
this.exportConfig.type,
'chart',
null,
null,
this.exportConfig.width,
this.exportConfig.height
);
}3. Memory Management:
ngOnDestroy() {
// Clear event listeners
this.subscriptions.forEach(sub => sub.unsubscribe());
// Destroy chart
if (this.chart) {
this.chart.destroy();
}
}Advanced Patterns
Pattern 1: Event-Driven Updates
export class EventDrivenChart {
private dataUpdateSubject = new Subject<any[]>();
ngOnInit() {
this.dataUpdateSubject.pipe(
debounceTime(300),
distinctUntilChanged()
).subscribe(data => {
this.chart.series[0].setData(data);
});
}
updateData(newData: any[]): void {
this.dataUpdateSubject.next(newData);
}
}Pattern 2: Conditional Export
onBeforeExport(args: IExportEventArgs): void {
// Check data quality
if (!this.validateDataQuality()) {
args.cancel = true;
this.showWarning('Data quality check failed');
return;
}
// Add watermark for drafts
if (this.isDraft) {
args.chart.annotations = [{
content: '<div style="opacity:0.3">DRAFT</div>',
x: '50%',
y: '50%',
coordinateUnits: 'Pixel'
}];
}
}Troubleshooting
Export Not Working
Issue: Export function not available Solutions:
- Import ExportService in providers
- Verify chart is fully loaded
- Check browser compatibility
- Ensure CORS for remote images
Events Not Firing
Issue: Event handlers not called Solutions:
- Verify event name spelling
- Check event is supported by chart type
- Ensure proper binding syntax
- Test in loaded event
Performance Issues
Issue: Slow rendering with events Solutions:
- Debounce frequent events
- Use Canvas rendering
- Minimize DOM manipulation in handlers
- Profile with Chrome DevTools
API Reference Summary
Event Interfaces
| Event | Interface | Description | API Reference |
|---|---|---|---|
| load | ILoadedEventArgs | Before chart loads | ILoadedEventArgs |
| loaded | ILoadedEventArgs | After chart loads | ILoadedEventArgs |
| pointRender | IPointRenderEventArgs | Before point rendering | IPointRenderEventArgs |
| seriesRender | ISeriesRenderEventArgs | Before series rendering | ISeriesRenderEventArgs |
| axisLabelRender | IAxisLabelRenderEventArgs | Before axis label rendering | IAxisLabelRenderEventArgs |
| legendRender | ILegendRenderEventArgs | Before legend rendering | ILegendRenderEventArgs |
| tooltipRender | ITooltipRenderEventArgs | Before tooltip rendering | ITooltipRenderEventArgs |
| pointClick | IPointEventArgs | Point click | IPointEventArgs |
| chartMouseClick | IMouseEventArgs | Chart click | IMouseEventArgs |
| chartMouseMove | IMouseEventArgs | Mouse move | IMouseEventArgs |
| beforeExport | IExportEventArgs | Before export | IExportEventArgs |
| afterExport | IExportEventArgs | After export | IExportEventArgs |
| beforePrint | IPrintEventArgs | Before print | IPrintEventArgs |
| animationComplete | IAnimationCompleteEventArgs | After animation | IAnimationCompleteEventArgs |
All Event Interfaces: See ChartModel for complete event list (40+ events)
Export and Print
| API | Description | Documentation |
|---|---|---|
| ExportType | Export format enum (PNG, JPEG, SVG, PDF) | exportType.md |
| Export | Export utility class | export.md |
| PrintUtils | Print utility functions | printUtils.md |
| IExportEventArgs | Export event interface | iExportEventArgs.md |
| IPrintEventArgs | Print event interface | iPrintEventArgs.md |
| IPDFArgs | PDF export arguments | iPDFArgs.md |
Rendering and Performance
| Property | Type | Description | API Reference |
|---|---|---|---|
| enableCanvas | boolean | Enable canvas rendering | ChartModel.enableCanvas |
| enableAnimation | boolean | Enable animations | ChartModel.enableAnimation |
| animation | AnimationModel | Animation configuration | AnimationModel |
Selection and Highlight
| API | Description | Documentation |
|---|---|---|
| SelectionMode | Selection mode enum (Point, Series, Cluster, DragXY, etc.) | selectionMode.md |
| SelectionPattern | Selection pattern enum | selectionPattern.md |
| HighlightMode | Highlight mode enum | highlightMode.md |
| Highlight | Highlight settings | highlight.md |
| Selection | Selection settings | selection.md |
| ISelectionCompleteEventArgs | Selection complete event | iSelectionCompleteEventArgs.md |
Chart Methods
| Method | Description | API Reference |
|---|---|---|
| export() | Export chart to image/PDF | ChartModel |
| print() | Print chart | ChartModel |
| refresh() | Refresh chart rendering | ChartModel |
| destroy() | Destroy chart instance | ChartModel |
Animation Configuration
| API | Description | Documentation |
|---|---|---|
| AnimationModel | Animation configuration interface | animationModel.md |
| Animation | Animation class | animation.md |
Drag Settings
| API | Description | Documentation |
|---|---|---|
| DragSettingsModel | Drag configuration interface | dragSettingsModel.md |
| DragSettings | Drag settings class | dragSettings.md |
| IDragCompleteEventArgs | Drag complete event | iDragCompleteEventArgs.md |
All Chart Events
Complete list of events available in ChartModel:
Lifecycle Events: load, loaded, animationComplete Rendering Events: seriesRender, pointRender, axisLabelRender, legendRender, textRender, annotationRender Interaction Events: pointClick, pointDoubleClick, pointMove, chartMouseClick, chartMouseMove, chartMouseDown, chartMouseUp, chartMouseLeave, chartDoubleClick Feature Events: tooltipRender, sharedTooltipRender, legendClick, axisLabelClick, multiLevelLabelClick Zoom Events: onZooming, zoomComplete Scroll Events: scrollStart, scrollEnd, scrollChanged Selection Events: selectionComplete Drag Events: drag, dragStart, dragEnd, dragComplete Export/Print Events: beforeExport, afterExport, beforePrint Resize Events: beforeResize, resized Data Events: axisRangeCalculated
---
Common Patterns Reference for Syncfusion Angular Chart
Table of Contents
- Introduction
- Trend Analysis Dashboard
- Financial Stock Chart
- Real-Time Monitoring Dashboard
- Multi-Axis Comparison Charts
- Drill-Down Charts
- Responsive Dashboard Layouts
- Performance Comparison Charts
- Time-Series Analysis
- Best Practices
- Troubleshooting
- Migration Guide
Introduction
This reference provides complete, production-ready patterns for common chart implementations. Each pattern includes full TypeScript code, styling, data handling, and integration guidance for building sophisticated data visualization solutions with Syncfusion Angular Charts.
Trend Analysis Dashboard
Create a comprehensive dashboard showing trends across multiple metrics with comparisons and forecasts.
Implementation
import { Component, OnInit, ViewChild } from '@angular/core';
import { ChartComponent } from '@syncfusion/ej2-angular-charts';
import { ChartModule, LineSeriesService, ColumnSeriesService, CategoryService, LegendService, TooltipService, DataLabelService, ZoomService, ScrollBarService, DateTimeService, AreaSeriesService, StackingColumnSeriesService, StackingAreaSeriesService, SplineSeriesService } from '@syncfusion/ej2-angular-charts';
import { ILoadedEventArgs, IPointRenderEventArgs } from '@syncfusion/ej2-charts';
@Component({
selector: 'app-trend-dashboard',
standalone: true,
imports: [ChartModule],
providers: [
LineSeriesService, ColumnSeriesService, CategoryService, LegendService, TooltipService, DataLabelService, ZoomService, ScrollBarService, DateTimeService, AreaSeriesService, StackingColumnSeriesService, StackingAreaSeriesService, SplineSeriesService
],
template: `
<div class="dashboard-container">
<div class="dashboard-header">
<h2>Sales Trend Analysis - 2024</h2>
<div class="controls">
<select (change)="changeTimeRange($event.target.value)">
<option value="monthly">Monthly</option>
<option value="quarterly">Quarterly</option>
<option value="yearly">Yearly</option>
</select>
<button (click)="exportDashboard()">Export Report</button>
</div>
</div>
<div class="kpi-cards">
<div class="kpi-card">
<h3>Total Sales</h3>
<p class="value">{{ totalSales | currency }}</p>
<span class="change positive">+15.3%</span>
</div>
<div class="kpi-card">
<h3>Average</h3>
<p class="value">{{ averageSales | currency }}</p>
<span class="change">+8.2%</span>
</div>
<div class="kpi-card">
<h3>Best Month</h3>
<p class="value">{{ bestMonth }}</p>
<span class="sub-text">{{ bestMonthValue | currency }}</span>
</div>
</div>
<div class="chart-grid">
<div class="chart-panel main-chart">
<ejs-chart #mainChart
[primaryXAxis]='primaryXAxis'
[primaryYAxis]='primaryYAxis'
[title]='chartTitle'
[tooltip]='tooltip'
[legendSettings]='legendSettings'
[crosshair]='crosshair'
(pointRender)='pointRender($event)'
(loaded)='onChartLoaded($event)'>
<e-series-collection>
<e-series
[dataSource]='actualData'
xName='month'
yName='sales'
name='Actual Sales'
type='Column'
[marker]='marker'
fill='#4CAF50'>
</e-series>
<e-series
[dataSource]='targetData'
xName='month'
yName='target'
name='Target'
type='Line'
width='3'
fill='#FF5722'
[marker]='targetMarker'>
</e-series>
<e-series
[dataSource]='forecastData'
xName='month'
yName='forecast'
name='Forecast'
type='Line'
width='2'
dashArray='5,5'
fill='#2196F3'
opacity='0.7'>
</e-series>
</e-series-collection>
</ejs-chart>
</div>
<div class="chart-panel">
<ejs-chart #growthChart
[primaryXAxis]='xAxis2'
[primaryYAxis]='yAxis2'
[title]='"Month-over-Month Growth"'
[tooltip]='tooltip'>
<e-series-collection>
<e-series
[dataSource]='growthData'
xName='month'
yName='growth'
type='Area'
fill='#9C27B0'
opacity='0.6'
[border]='areaBorder'>
</e-series>
</e-series-collection>
</ejs-chart>
</div>
<div class="chart-panel">
<ejs-chart #categoryChart
[primaryXAxis]='categoryXAxis'
[primaryYAxis]='categoryYAxis'
[title]='"Sales by Category"'
[tooltip]='tooltip'
[legendSettings]='legendSettings'>
<e-series-collection>
<e-series
[dataSource]='categoryData'
xName='category'
yName='value'
type='StackingColumn'
name='Q1'>
</e-series>
<e-series
[dataSource]='categoryData'
xName='category'
yName='value2'
type='StackingColumn'
name='Q2'>
</e-series>
<e-series
[dataSource]='categoryData'
xName='category'
yName='value3'
type='StackingColumn'
name='Q3'>
</e-series>
</e-series-collection>
</ejs-chart>
</div>
</div>
</div>
`,
styles: [`
.dashboard-container {
padding: 20px;
background: #f5f5f5;
}
.dashboard-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 20px;
padding: 20px;
background: white;
border-radius: 8px;
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
}
.controls select, .controls button {
margin-left: 10px;
padding: 8px 16px;
border-radius: 4px;
border: 1px solid #ddd;
}
.kpi-cards {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
gap: 20px;
margin-bottom: 20px;
}
.kpi-card {
background: white;
padding: 20px;
border-radius: 8px;
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
}
.kpi-card h3 {
margin: 0 0 10px 0;
color: #666;
font-size: 14px;
}
.kpi-card .value {
font-size: 32px;
font-weight: bold;
margin: 10px 0;
color: #333;
}
.kpi-card .change {
padding: 4px 8px;
border-radius: 4px;
font-size: 14px;
}
.kpi-card .change.positive {
background: #E8F5E9;
color: #4CAF50;
}
.chart-grid {
display: grid;
grid-template-columns: 2fr 1fr;
gap: 20px;
}
.chart-panel {
background: white;
padding: 20px;
border-radius: 8px;
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
}
.main-chart {
grid-column: 1 / -1;
}
@media (max-width: 768px) {
.chart-grid {
grid-template-columns: 1fr;
}
.main-chart {
grid-column: 1;
}
}
`]
})
export class TrendDashboardComponent implements OnInit {
@ViewChild('mainChart') mainChart: ChartComponent;
@ViewChild('growthChart') growthChart: ChartComponent;
@ViewChild('categoryChart') categoryChart: ChartComponent;
public actualData: Object[] = [];
public targetData: Object[] = [];
public forecastData: Object[] = [];
public growthData: Object[] = [];
public categoryData: Object[] = [];
public totalSales: number = 0;
public averageSales: number = 0;
public bestMonth: string = '';
public bestMonthValue: number = 0;
public chartTitle: string = 'Monthly Sales Performance';
public primaryXAxis: Object = {
valueType: 'Category',
title: 'Month',
labelRotation: -45,
majorGridLines: { width: 0 }
};
public primaryYAxis: Object = {
title: 'Sales ($)',
labelFormat: '${value}K',
majorGridLines: { width: 1, color: '#E0E0E0' }
};
public xAxis2: Object = { valueType: 'Category' };
public yAxis2: Object = { labelFormat: '{value}%' };
public categoryXAxis: Object = { valueType: 'Category' };
public categoryYAxis: Object = { labelFormat: '${value}K' };
public tooltip: Object = {
enable: true,
shared: true,
format: '<b>${point.x}</b><br/>${series.name}: <b>${point.y}</b>'
};
public legendSettings: Object = {
visible: true,
position: 'Bottom'
};
public crosshair: Object = {
enable: true,
lineType: 'Vertical'
};
public marker: Object = {
visible: true,
height: 8,
width: 8,
dataLabel: {
visible: true,
position: 'Top',
font: { fontWeight: '600', color: '#ffffff' }
}
};
public targetMarker: Object = {
visible: true,
shape: 'Diamond',
height: 10,
width: 10
};
public areaBorder: Object = {
width: 2,
color: '#9C27B0'
};
ngOnInit(): void {
this.loadData();
this.calculateKPIs();
}
loadData(): void {
// Actual sales data
this.actualData = [
{ month: 'Jan', sales: 35 },
{ month: 'Feb', sales: 28 },
{ month: 'Mar', sales: 34 },
{ month: 'Apr', sales: 32 },
{ month: 'May', sales: 40 },
{ month: 'Jun', sales: 45 },
{ month: 'Jul', sales: 42 },
{ month: 'Aug', sales: 48 },
{ month: 'Sep', sales: 50 },
{ month: 'Oct', sales: 46 },
{ month: 'Nov', sales: 52 },
{ month: 'Dec', sales: 55 }
];
// Target data
this.targetData = [
{ month: 'Jan', target: 38 },
{ month: 'Feb', target: 30 },
{ month: 'Mar', target: 36 },
{ month: 'Apr', target: 34 },
{ month: 'May', target: 38 },
{ month: 'Jun', target: 42 },
{ month: 'Jul', target: 45 },
{ month: 'Aug', target: 47 },
{ month: 'Sep', target: 48 },
{ month: 'Oct', target: 50 },
{ month: 'Nov', target: 51 },
{ month: 'Dec', target: 53 }
];
// Forecast (next 3 months)
this.forecastData = [
{ month: 'Oct', forecast: 46 },
{ month: 'Nov', forecast: 52 },
{ month: 'Dec', forecast: 55 },
{ month: 'Jan 2025', forecast: 58 },
{ month: 'Feb 2025', forecast: 60 },
{ month: 'Mar 2025', forecast: 62 }
];
// Growth data
this.growthData = this.calculateGrowth(this.actualData);
// Category data
this.categoryData = [
{ category: 'Electronics', value: 120, value2: 135, value3: 150 },
{ category: 'Clothing', value: 80, value2: 85, value3: 90 },
{ category: 'Food', value: 60, value2: 65, value3: 70 },
{ category: 'Books', value: 45, value2: 50, value3: 48 }
];
}
calculateGrowth(data: any[]): any[] {
return data.map((item, index) => {
if (index === 0) return { month: item.month, growth: 0 };
let prevValue = data[index - 1].sales;
let growth = ((item.sales - prevValue) / prevValue) * 100;
return { month: item.month, growth: growth };
});
}
calculateKPIs(): void {
this.totalSales = this.actualData.reduce((sum, item: any) => sum + item.sales, 0);
this.averageSales = this.totalSales / this.actualData.length;
let maxItem = this.actualData.reduce((max: any, item: any) =>
item.sales > max.sales ? item : max
);
this.bestMonth = maxItem.month;
this.bestMonthValue = maxItem.sales;
}
pointRender(args: IPointRenderEventArgs): void {
// Highlight points below target
if (args.series.name === 'Actual Sales') {
let target = this.targetData.find((t: any) => t.month === args.point.x);
if (target && args.point.y < target.target) {
args.fill = '#FF9800'; // Orange for below target
}
}
}
changeTimeRange(range: string): void {
// Implement time range change logic
console.log('Changing to:', range);
}
onChartLoaded(args: ILoadedEventArgs): void {
console.log('Chart loaded successfully');
}
exportDashboard(): void {
this.mainChart.export('PDF', 'TrendAnalysis', 'Landscape', [
this.mainChart,
this.growthChart,
this.categoryChart
]);
}
}Financial Stock Chart
Implement a professional stock chart with candlestick patterns and technical indicators.
import { Component, OnInit, ViewChild } from '@angular/core';
import { ChartComponent } from '@syncfusion/ej2-angular-charts';
import { ChartModule, LineSeriesService, ColumnSeriesService, CategoryService, LegendService, TooltipService, DataLabelService, ZoomService, ScrollBarService, DateTimeService, AreaSeriesService, StackingColumnSeriesService, StackingAreaSeriesService, SplineSeriesService } from '@syncfusion/ej2-angular-charts';
import { ILoadedEventArgs, IPointRenderEventArgs } from '@syncfusion/ej2-charts';
@Component({
selector: 'app-stock-chart',
standalone: true,
imports: [ChartModule],
providers: [
LineSeriesService, ColumnSeriesService, CategoryService, LegendService, TooltipService, DataLabelService, ZoomService, ScrollBarService, DateTimeService, AreaSeriesService, StackingColumnSeriesService, StackingAreaSeriesService, SplineSeriesService
],
template: `
<div class="stock-container">
<div class="stock-header">
<div class="stock-info">
<h2>{{ stockSymbol }}</h2>
<div class="price">
<span class="current">\${{ currentPrice }}</span>
<span [class]="priceChange >= 0 ? 'positive' : 'negative'">
{{ priceChange >= 0 ? '+' : '' }}{{ priceChange.toFixed(2) }}
({{ priceChangePercent.toFixed(2) }}%)
</span>
</div>
</div>
<div class="controls">
<button *ngFor="let range of timeRanges"
(click)="changeRange(range)"
[class.active]="selectedRange === range">
{{ range }}
</button>
</div>
</div>
<ejs-chart #stockChart
[primaryXAxis]='primaryXAxis'
[primaryYAxis]='primaryYAxis'
[tooltip]='tooltip'
[crosshair]='crosshair'
[zoomSettings]='zoomSettings'
[legendSettings]='legendSettings'
[height]='chartHeight'
(loaded)='onChartLoaded($event)'>
<e-series-collection>
<!-- Candlestick Series -->
<e-series
[dataSource]='stockData'
xName='date'
high='high'
low='low'
open='open'
close='close'
type='Candle'
name='{{ stockSymbol }}'
bearFillColor='#F44336'
bullFillColor='#4CAF50'>
</e-series>
<!-- Volume -->
<e-series
[dataSource]='stockData'
xName='date'
yName='volume'
type='Column'
name='Volume'
yAxisName='volumeAxis'
opacity='0.5'
fill='#9E9E9E'>
</e-series>
<!-- Moving Average 20 -->
<e-series
[dataSource]='ma20Data'
xName='date'
yName='value'
type='Line'
name='MA(20)'
width='2'
fill='#2196F3'
dashArray='5,5'>
</e-series>
<!-- Moving Average 50 -->
<e-series
[dataSource]='ma50Data'
xName='date'
yName='value'
type='Line'
name='MA(50)'
width='2'
fill='#FF9800'>
</e-series>
</e-series-collection>
<e-axes>
<e-axis
name='volumeAxis'
opposedPosition='true'
majorGridLines='{ width: 0 }'
labelFormat='{value}M'
minimum='0'
maximum='1000'
interval='250'>
</e-axis>
</e-axes>
</ejs-chart>
<div class="indicators">
<div class="indicator">
<span>Open:</span> \${{ latestData.open }}
</div>
<div class="indicator">
<span>High:</span> \${{ latestData.high }}
</div>
<div class="indicator">
<span>Low:</span> \${{ latestData.low }}
</div>
<div class="indicator">
<span>Close:</span> \${{ latestData.close }}
</div>
<div class="indicator">
<span>Volume:</span> {{ latestData.volume }}M
</div>
</div>
</div>
`,
styles: [`
.stock-container {
padding: 20px;
background: #fff;
}
.stock-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 20px;
}
.stock-info h2 {
margin: 0;
font-size: 24px;
}
.price {
margin-top: 10px;
}
.price .current {
font-size: 32px;
font-weight: bold;
margin-right: 10px;
}
.price .positive {
color: #4CAF50;
}
.price .negative {
color: #F44336;
}
.controls button {
margin-left: 5px;
padding: 8px 16px;
border: 1px solid #ddd;
background: white;
cursor: pointer;
border-radius: 4px;
}
.controls button.active {
background: #2196F3;
color: white;
border-color: #2196F3;
}
.indicators {
display: flex;
justify-content: space-around;
margin-top: 20px;
padding: 15px;
background: #f5f5f5;
border-radius: 4px;
}
.indicator span {
font-weight: bold;
margin-right: 5px;
}
`]
})
export class StockChartComponent implements OnInit {
@ViewChild('stockChart') stockChart: ChartComponent;
public stockSymbol: string = 'AAPL';
public currentPrice: number = 175.50;
public priceChange: number = 2.35;
public priceChangePercent: number = 1.35;
public chartHeight: string = '500px';
public stockData: Object[] = [];
public ma20Data: Object[] = [];
public ma50Data: Object[] = [];
public latestData: any = {};
public timeRanges: string[] = ['1D', '5D', '1M', '3M', '6M', '1Y', '5Y'];
public selectedRange: string = '1M';
public primaryXAxis: Object = {
valueType: 'DateTime',
majorGridLines: { width: 0 },
crosshairTooltip: { enable: true }
};
public primaryYAxis: Object = {
title: 'Price ($)',
labelFormat: '${value}',
opposedPosition: true,
lineStyle: { width: 0 }
};
public tooltip: Object = {
enable: true,
shared: true
};
public crosshair: Object = {
enable: true,
lineType: 'Both'
};
public zoomSettings: Object = {
enableSelectionZooming: true,
enablePan: true,
enableMouseWheelZooming: true,
mode: 'X'
};
public legendSettings: Object = {
visible: true
};
ngOnInit(): void {
this.loadStockData();
this.calculateMovingAverages();
}
loadStockData(): void {
// Generate sample stock data
const startDate = new Date(2024, 0, 1);
const dataPoints = 90;
let basePrice = 170;
this.stockData = [];
for (let i = 0; i < dataPoints; i++) {
const date = new Date(startDate);
date.setDate(date.getDate() + i);
const open = basePrice + (Math.random() * 10 - 5);
const close = open + (Math.random() * 8 - 4);
const high = Math.max(open, close) + Math.random() * 3;
const low = Math.min(open, close) - Math.random() * 3;
const volume = Math.floor(Math.random() * 500 + 200);
this.stockData.push({
date: date,
open: parseFloat(open.toFixed(2)),
high: parseFloat(high.toFixed(2)),
low: parseFloat(low.toFixed(2)),
close: parseFloat(close.toFixed(2)),
volume: volume
});
basePrice = close;
}
this.latestData = this.stockData[this.stockData.length - 1];
this.currentPrice = this.latestData.close;
}
calculateMovingAverages(): void {
this.ma20Data = this.calculateMA(this.stockData, 20);
this.ma50Data = this.calculateMA(this.stockData, 50);
}
calculateMA(data: any[], period: number): any[] {
const ma = [];
for (let i = period - 1; i < data.length; i++) {
const sum = data.slice(i - period + 1, i + 1)
.reduce((acc, item) => acc + item.close, 0);
ma.push({
date: data[i].date,
value: parseFloat((sum / period).toFixed(2))
});
}
return ma;
}
changeRange(range: string): void {
this.selectedRange = range;
// Reload data based on range
this.loadStockData();
this.calculateMovingAverages();
}
onChartLoaded(args: any): void {
console.log('Stock chart loaded');
}
}Real-Time Monitoring Dashboard
Create a live dashboard that updates with real-time data streams.
import { Component, OnInit, OnDestroy, ViewChild } from '@angular/core';
import { ChartComponent } from '@syncfusion/ej2-angular-charts';
import { interval, Subscription } from 'rxjs';
import { ChartModule, LineSeriesService, ColumnSeriesService, CategoryService, LegendService, TooltipService, DataLabelService, ZoomService, ScrollBarService, DateTimeService, AreaSeriesService, StackingColumnSeriesService, StackingAreaSeriesService, SplineSeriesService } from '@syncfusion/ej2-angular-charts';
import { ILoadedEventArgs, IPointRenderEventArgs } from '@syncfusion/ej2-charts';
@Component({
selector: 'app-realtime-monitor',
standalone: true,
imports: [ChartModule],
providers: [
LineSeriesService, ColumnSeriesService, CategoryService, LegendService, TooltipService, DataLabelService, ZoomService, ScrollBarService, DateTimeService, AreaSeriesService, StackingColumnSeriesService, StackingAreaSeriesService, SplineSeriesService
],
template: `
<div class="monitor-container">
<div class="monitor-header">
<h2>System Performance Monitor</h2>
<div class="status">
<span class="indicator" [class.active]="isLive"></span>
<span>{{ isLive ? 'LIVE' : 'PAUSED' }}</span>
<button (click)="toggleLive()">
{{ isLive ? 'Pause' : 'Resume' }}
</button>
</div>
</div>
<div class="metrics-grid">
<div class="metric-card">
<h3>CPU Usage</h3>
<ejs-chart #cpuChart
[primaryXAxis]='xAxis'
[primaryYAxis]='cpuYAxis'
[height]='"200px"'
[chartArea]='chartArea'>
<e-series-collection>
<e-series
[dataSource]='cpuData'
xName='time'
yName='value'
type='SplineArea'
fill='#FF6B6B'
opacity='0.6'
[animation]='animation'>
</e-series>
</e-series-collection>
</ejs-chart>
<div class="current-value">{{ currentCPU }}%</div>
</div>
<div class="metric-card">
<h3>Memory Usage</h3>
<ejs-chart #memoryChart
[primaryXAxis]='xAxis'
[primaryYAxis]='memoryYAxis'
[height]='"200px"'
[chartArea]='chartArea'>
<e-series-collection>
<e-series
[dataSource]='memoryData'
xName='time'
yName='value'
type='SplineArea'
fill='#4ECDC4'
opacity='0.6'
[animation]='animation'>
</e-series>
</e-series-collection>
</ejs-chart>
<div class="current-value">{{ currentMemory }}%</div>
</div>
<div class="metric-card">
<h3>Network Traffic</h3>
<ejs-chart #networkChart
[primaryXAxis]='xAxis'
[primaryYAxis]='networkYAxis'
[height]='"200px"'
[chartArea]='chartArea'>
<e-series-collection>
<e-series
[dataSource]='networkData'
xName='time'
yName='value'
type='SplineArea'
fill='#95E1D3'
opacity='0.6'
[animation]='animation'>
</e-series>
</e-series-collection>
</ejs-chart>
<div class="current-value">{{ currentNetwork }} MB/s</div>
</div>
<div class="metric-card">
<h3>Disk I/O</h3>
<ejs-chart #diskChart
[primaryXAxis]='xAxis'
[primaryYAxis]='diskYAxis'
[height]='"200px"'
[chartArea]='chartArea'>
<e-series-collection>
<e-series
[dataSource]='diskData'
xName='time'
yName='value'
type='SplineArea'
fill='#F38181'
opacity='0.6'
[animation]='animation'>
</e-series>
</e-series-collection>
</ejs-chart>
<div class="current-value">{{ currentDisk }} MB/s</div>
</div>
</div>
<div class="alerts-panel">
<h3>Recent Alerts</h3>
<div class="alert" *ngFor="let alert of alerts" [class]="alert.severity">
<span class="time">{{ alert.time | date:'HH:mm:ss' }}</span>
<span class="message">{{ alert.message }}</span>
</div>
</div>
</div>
`,
styles: [`
.monitor-container {
padding: 20px;
background: #1a1a1a;
color: #fff;
min-height: 100vh;
}
.monitor-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 30px;
}
.status {
display: flex;
align-items: center;
gap: 10px;
}
.indicator {
width: 12px;
height: 12px;
border-radius: 50%;
background: #666;
}
.indicator.active {
background: #4CAF50;
animation: pulse 2s infinite;
}
@keyframes pulse {
0%, 100% { opacity: 1; }
50% { opacity: 0.5; }
}
.metrics-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
gap: 20px;
margin-bottom: 30px;
}
.metric-card {
background: #2a2a2a;
padding: 20px;
border-radius: 8px;
position: relative;
}
.metric-card h3 {
margin: 0 0 15px 0;
font-size: 14px;
color: #888;
}
.current-value {
position: absolute;
top: 20px;
right: 20px;
font-size: 24px;
font-weight: bold;
}
.alerts-panel {
background: #2a2a2a;
padding: 20px;
border-radius: 8px;
}
.alert {
padding: 10px;
margin: 10px 0;
border-left: 4px solid;
background: #333;
}
.alert.warning {
border-color: #FFC107;
}
.alert.error {
border-color: #F44336;
}
.alert .time {
margin-right: 10px;
color: #888;
}
`]
})
export class RealtimeMonitorComponent implements OnInit, OnDestroy {
@ViewChild('cpuChart') cpuChart: ChartComponent;
@ViewChild('memoryChart') memoryChart: ChartComponent;
@ViewChild('networkChart') networkChart: ChartComponent;
@ViewChild('diskChart') diskChart: ChartComponent;
public isLive: boolean = true;
private dataSubscription: Subscription;
private maxDataPoints: number = 30;
public cpuData: Object[] = [];
public memoryData: Object[] = [];
public networkData: Object[] = [];
public diskData: Object[] = [];
public currentCPU: number = 0;
public currentMemory: number = 0;
public currentNetwork: number = 0;
public currentDisk: number = 0;
public alerts: any[] = [];
public xAxis: Object = {
valueType: 'DateTime',
labelFormat: 'HH:mm:ss',
majorGridLines: { width: 0 },
visible: false
};
public cpuYAxis: Object = {
minimum: 0,
maximum: 100,
interval: 25,
labelFormat: '{value}%',
majorGridLines: { width: 1, color: '#333' }
};
public memoryYAxis: Object = {
minimum: 0,
maximum: 100,
interval: 25,
labelFormat: '{value}%',
majorGridLines: { width: 1, color: '#333' }
};
public networkYAxis: Object = {
minimum: 0,
maximum: 100,
interval: 25,
labelFormat: '{value}',
majorGridLines: { width: 1, color: '#333' }
};
public diskYAxis: Object = {
minimum: 0,
maximum: 100,
interval: 25,
labelFormat: '{value}',
majorGridLines: { width: 1, color: '#333' }
};
public chartArea: Object = {
border: { width: 0 },
background: 'transparent'
};
public animation: Object = {
enable: false
};
ngOnInit(): void {
this.initializeData();
this.startDataStream();
}
ngOnDestroy(): void {
if (this.dataSubscription) {
this.dataSubscription.unsubscribe();
}
}
initializeData(): void {
const now = new Date();
for (let i = this.maxDataPoints - 1; i >= 0; i--) {
const time = new Date(now.getTime() - i * 1000);
this.cpuData.push({ time: time, value: 0 });
this.memoryData.push({ time: time, value: 0 });
this.networkData.push({ time: time, value: 0 });
this.diskData.push({ time: time, value: 0 });
}
}
startDataStream(): void {
this.dataSubscription = interval(1000).subscribe(() => {
if (this.isLive) {
this.updateData();
}
});
}
updateData(): void {
const now = new Date();
// Generate random data
this.currentCPU = parseFloat((Math.random() * 100).toFixed(1));
this.currentMemory = parseFloat((Math.random() * 100).toFixed(1));
this.currentNetwork = parseFloat((Math.random() * 100).toFixed(1));
this.currentDisk = parseFloat((Math.random() * 100).toFixed(1));
// Add new data points
this.cpuData.push({ time: now, value: this.currentCPU });
this.memoryData.push({ time: now, value: this.currentMemory });
this.networkData.push({ time: now, value: this.currentNetwork });
this.diskData.push({ time: now, value: this.currentDisk });
// Remove old data points
if (this.cpuData.length > this.maxDataPoints) {
this.cpuData.shift();
this.memoryData.shift();
this.networkData.shift();
this.diskData.shift();
}
// Check thresholds and create alerts
this.checkThresholds();
// Update charts
this.refreshCharts();
}
checkThresholds(): void {
if (this.currentCPU > 80) {
this.addAlert('error', 'High CPU usage detected: ' + this.currentCPU + '%');
}
if (this.currentMemory > 85) {
this.addAlert('warning', 'High memory usage: ' + this.currentMemory + '%');
}
}
addAlert(severity: string, message: string): void {
this.alerts.unshift({
time: new Date(),
severity: severity,
message: message
});
// Keep only last 5 alerts
if (this.alerts.length > 5) {
this.alerts.pop();
}
}
refreshCharts(): void {
if (this.cpuChart) this.cpuChart.refresh();
if (this.memoryChart) this.memoryChart.refresh();
if (this.networkChart) this.networkChart.refresh();
if (this.diskChart) this.diskChart.refresh();
}
toggleLive(): void {
this.isLive = !this.isLive;
}
}Multi-Axis Comparison Charts
Compare different metrics with different scales on the same chart.
import { ChartModule } from '@syncfusion/ej2-angular-charts'
import { CategoryService, ColumnSeriesService, LineSeriesService } from '@syncfusion/ej2-angular-charts'
import { Component, OnInit } from '@angular/core';
@Component({
imports: [
ChartModule
],
providers: [ CategoryService, ColumnSeriesService, LineSeriesService],
standalone: true,
selector: 'app-container',
template: `<ejs-chart id="chart-container" [primaryXAxis]='primaryXAxis'[primaryYAxis]='primaryYAxis' [title]='title'>
<e-axes>
<e-axis rowIndex=1 name='yAxis1' opposedPosition='true' title='Temperature (Celsius)' [majorGridLines]='majorGridLines' labelFormat='{value}°C'
[minimum]='24' [maximum]='36' [interval]='2' [lineStyle]='lineStyle'>
</e-axis>
</e-axes>
<e-rows>
<e-row height=50%></e-row>
<e-row height=50%></e-row>
</e-rows>
<e-series-collection>
<e-series [dataSource]='chartData' type='Column' xName='x' yName='y' name='Germany'></e-series>
<e-series [dataSource]='chartData' type='Line' xName='x' yName='y1' name='Japan' yAxisName='yAxis1' [marker]='marker'></e-series>
</e-series-collection>
</ejs-chart>`
})
export class AppComponent implements OnInit {
public primaryXAxis?: Object;
public chartData?: Object[];
public title?: string;
public majorGridLines?: Object;
public primaryYAxis?: Object;
public lineStyle?: Object;
public marker?: Object;
public rows?: Object;
ngOnInit(): void {
this.chartData = [
{ x: 'Jan', y: 15, y1: 33 }, { x: 'Feb', y: 20, y1: 31 }, { x: 'Mar', y: 35, y1: 30 },
{ x: 'Apr', y: 40, y1: 28 }, { x: 'May', y: 80, y1: 29 }, { x: 'Jun', y: 70, y1: 30 },
{ x: 'Jul', y: 65, y1: 33 }, { x: 'Aug', y: 55, y1: 32 }, { x: 'Sep', y: 50, y1: 34 },
{ x: 'Oct', y: 30, y1: 32 }, { x: 'Nov', y: 35, y1: 32 }, { x: 'Dec', y: 35, y1: 31 }
];
this.primaryXAxis = {
title: 'Months',
valueType: 'Category',
interval: 1
};
this.primaryYAxis = {
minimum: 0, maximum: 90, interval: 20,
lineStyle: { width: 0 },
title: 'Temperature (Fahrenheit)',
labelFormat: '{value}°F'
};
this.majorGridLines = { width: 0};
this.lineStyle = { width: 0};
this.marker = {
visible: true, width: 10, height: 10, border: { width: 2, color: '#F8AB1D' }
}
this.title = 'Weather Condition';
}
}Best Practices
1. Performance Optimization
// Use Canvas for large datasets
<ejs-chart [enableCanvas]='true' *ngIf="dataPoints > 5000">2. Memory Management
ngOnDestroy() {
this.subscriptions.forEach(sub => sub.unsubscribe());
if (this.chart) {
this.chart.destroy();
}
}3. Responsive Design
@HostListener('window:resize')
onResize() {
if (this.chart) {
this.chart.refresh();
}
}Troubleshooting
Issue: Chart not updating with new data
Solution: Call refresh() after data changes or use setData() method.
Issue: Performance degradation
Solution: Enable canvas rendering, reduce animations, implement data virtualization.
Issue: Legend items not interactive
Solution: Ensure LegendService is provided in module/component.
Migration Guide
From EJ1 to EJ2
Property Changes:
// EJ1
<ej-chart [series]="[{ points: data }]">
// EJ2
<ejs-chart>
<e-series-collection>
<e-series [dataSource]='data'>
</e-series-collection>
</ejs-chart>Event Changes:
// EJ1
(load)="chartLoad($event)"
// EJ2
(loaded)="chartLoaded($event)"---
Critical: Data Label API Hierarchy Pattern
⚠️ Common Mistake to Avoid
INCORRECT: Creating dataLabel as independent series property
❌ WRONG
public dataLabelSettings = {
visible: true,
position: 'Top',
format: '${point.y}K'
};
// Template:
<e-series [dataLabel]="dataLabelSettings"></e-series> <!-- ❌ Property doesn't exist -->CORRECT: dataLabel must be NESTED inside marker
✅ CORRECT
public markerSettings = {
visible: false, // Hide marker dots
dataLabel: { // ← NESTED INSIDE MARKER
visible: true,
position: 'Top',
format: '${point.y}K',
font: { size: '12px', fontWeight: '600' }
}
};
// Template:
<e-series [marker]="markerSettings"></e-series> <!-- ✅ Correct binding -->API Hierarchy Structure
marker (root property on series)
├─ visible: boolean (show/hide marker dot)
├─ width: number
├─ height: number
├─ shape: string
├─ fill: string
├─ border: { ... }
└─ dataLabel (NESTED OBJECT) ← Key point
├─ visible: boolean
├─ position: string
├─ format: string
├─ font: { size, color, fontWeight }
└─ alignment: stringCode Example: Stacked Column Chart
import { MarkerSettingsModel } from '@syncfusion/ej2-angular-charts';
export class StackedColumnChartComponent {
// ✅ CORRECT STRUCTURE
public markerSettings: MarkerSettingsModel = {
visible: false, // Don't show marker dots
dataLabel: { // ← NESTED
visible: true,
position: 'Top',
format: '${point.y}K',
font: {
size: '12px',
fontWeight: '600',
color: '#1f2937'
}
}
};
}<ejs-chart>
<e-series-collection>
<e-series
[dataSource]="quarterlyData"
type="StackingColumn"
xName="month"
yName="q1"
[marker]="markerSettings"> <!-- ✅ Bind [marker] not [dataLabel] -->
</e-series>
</e-series-collection>
</ejs-chart>Verification Checklist
- [ ] Property name is
marker, notdataLabel - [ ]
dataLabelis nested INSIDEmarkerobject - [ ] Template uses
[marker]="markerSettings"binding - [ ] NOT using
[dataLabel]binding - [ ] Refer to
chart-elements.mdfor complete examples
Key Lesson
Always read complete code examples from skill file, not just keyword search. Syncfusion's API hierarchy is:
marker= root propertydataLabel= nested configuration[marker]="..."= template binding
This pattern applies to many Syncfusion components where nested objects contain feature-specific settings.
---