@rm-graph/angular
v0.1.26
Published
RM-Graphs Angular - Angular components for RM-Graphs charting library
Maintainers
Readme
@rm-graph/angular
RM-Graphs Angular - Angular components for RM-Graphs charting library.
Installation
npm install @rm-graph/angular
# or just the core for direct usage
npm install @rm-graph/core
scichartis automatically installed as a dependency!
Usage Options
Option 1: Using Core Library Directly (Recommended)
The most reliable approach is to use the core library directly in your Angular components:
import { Component, ElementRef, ViewChild, AfterViewInit, OnDestroy } from '@angular/core';
import {
Column3DChart,
createColumn3DChart,
} from '@rm-graph/core';
@Component({
selector: 'app-chart',
standalone: true,
template: `<div #chartContainer style="width: 100%; height: 400px;"></div>`
})
export class ChartComponent implements AfterViewInit, OnDestroy {
@ViewChild('chartContainer') chartRef!: ElementRef<HTMLDivElement>;
private chart: Column3DChart | null = null;
chartData = [[45, 1500, 1], [90, 2200, 2], [180, 1800, 3]];
async ngAfterViewInit(): Promise<void> {
this.chart = await createColumn3DChart(this.chartRef.nativeElement, {
theme: 'dark',
data: this.chartData,
xAxisTitle: 'Phase Angle (°)',
yAxisTitle: 'Amplitude (mVp)',
zAxisTitle: 'Cycle',
});
}
ngOnDestroy(): void {
this.chart?.destroy();
}
// Update data dynamically
updateData(newData: number[][]): void {
this.chart?.setData(newData);
}
// Change theme
setTheme(theme: string): void {
this.chart?.setOptions({ theme });
}
}Option 2: Using Angular Components (Standalone)
Import the pre-built Angular components:
import { Component } from '@angular/core';
import { Column3DChartComponent, PRPDChartComponent, TrendsChartComponent } from '@rm-graph/angular';
@Component({
selector: 'app-dashboard',
standalone: true,
imports: [Column3DChartComponent, PRPDChartComponent, TrendsChartComponent],
template: `
<sui-column3d-chart
[data]="chartData"
height="400px"
theme="dark"
xAxisTitle="Phase Angle (°)"
yAxisTitle="Amplitude (mVp)"
zAxisTitle="Cycle"
(ready)="onChartReady($event)"
/>
`
})
export class DashboardComponent {
chartData = [[45, 1500, 1], [90, 2200, 2], [180, 1800, 3]];
onChartReady(chart: any) {
console.log('Chart ready!', chart);
}
}Components
<rm-column3d-chart>
| Input | Type | Default | Description |
|-------|------|---------|-------------|
| data | number[][] | [] | Chart data as [x, y, z] tuples |
| width | string | '100%' | Chart width |
| height | string | '400px' | Chart height |
| theme | string \| ThemeConfig | - | Theme |
| xAxisTitle | string | - | X-axis title |
| yAxisTitle | string | - | Y-axis title |
| zAxisTitle | string | - | Z-axis title |
| Output | Type | Description |
|--------|------|-------------|
| ready | EventEmitter<Column3DChart> | Emitted when chart is ready |
| chartError | EventEmitter<Error> | Emitted on error |
Chart Types Available from Core
createColumn3DChart- 3D column charts for phase-resolved datacreatePRPDChart- Phase Resolved Partial Discharge heatmapscreateSurface3DChart- 3D surface visualization
PRPDChart
The PRPDChartComponent displays Phase Resolved Partial Discharge data as an interactive heatmap.
Usage
import { Component } from '@angular/core';
import { PRPDChartComponent } from '@rm-graph/angular';
import type { PRPDDataPoint, PRPDChartStats } from '@rm-graph/core';
@Component({
selector: 'app-dashboard',
standalone: true,
imports: [PRPDChartComponent],
template: `
<sui-prpd-chart
id="my-prpd-chart"
title="PRPD Analysis"
[data]="prpdData"
height="400px"
[scalingFactor]="1"
unitOfMeasurement="mVp"
[maxPeak]="5000"
[minPeak]="-5000"
[maxPhaseAngle]="360"
[showSineWave]="true"
[showColorPalette]="true"
[showMaximizeIcon]="true"
[colorMin]="0"
[colorMax]="100"
(statsChange)="onStatsChange($event)"
/>
`
})
export class DashboardComponent {
// Data format: [phaseAngle, amplitude, count][]
prpdData: PRPDDataPoint[] = [
[45, 1500, 10], // 45° phase, 1500mV amplitude, 10 pulses
[90, 2200, 25], // 90° phase, 2200mV amplitude, 25 pulses
[180, -1800, 15], // 180° phase, -1800mV amplitude, 15 pulses
[270, -2500, 30], // 270° phase, -2500mV amplitude, 30 pulses
];
onStatsChange(stats: PRPDChartStats) {
console.log('Peak:', stats.peakValue, 'Total:', stats.totalCount);
}
}PRPDDataPoint Type
type PRPDDataPoint = [number, number, number];
// [phaseAngle, amplitude, count]| Index | Type | Description |
|-------|------|-------------|
| 0 | number | Phase angle in degrees (0-360) |
| 1 | number | Amplitude value (positive or negative) |
| 2 | number | Pulse count at this phase/amplitude |
Inputs
| Input | Type | Default | Description |
|-------|------|---------|-------------|
| id | string | auto-generated | Unique ID for the chart |
| title | string | - | Chart title displayed in header |
| data | PRPDDataPoint[] | [] | Array of [phase, amplitude, count] tuples |
| height | string | '400px' | Chart height |
| width | string | '100%' | Chart width |
| scalingFactor | number | 1 | Multiplier for amplitude values |
| unitOfMeasurement | string | 'mVp' | Unit label for amplitude axis |
| maxPeak | number | 5000 | Maximum amplitude value |
| minPeak | number | -5000 | Minimum amplitude value |
| maxPhaseAngle | number | 360 | Maximum phase angle |
| yAxisRange | number | - | Y-axis visible range |
| showSineWave | boolean | true | Show sine wave overlay |
| showColorPalette | boolean | true | Show color legend bar |
| showMaximizeIcon | boolean | false | Show maximize button |
| isMaximized | boolean | false | Current maximized state |
| colorMin | number | 0 | Minimum color scale value |
| colorMax | number | 100 | Maximum color scale value |
| resolutionLabel | PRPDResolutionLabel | - | Resolution mode config |
| windowingData | PRPDWindowingRegion[] | - | Windowing filter regions |
Outputs
| Output | Type | Description |
|--------|------|-------------|
| ready | EventEmitter<PRPDChart> | Emitted when chart is initialized |
| statsChange | EventEmitter<PRPDChartStats> | Emitted when statistics update |
| resolutionChange | EventEmitter<PRPDResolutionLabel> | Emitted when resolution mode changes |
| windowingClick | EventEmitter<void> | Emitted when windowing button clicked |
| yAxisRangeChange | EventEmitter<number> | Emitted when Y-range changes |
| maximizeToggle | EventEmitter<void> | Emitted when maximize toggled |
Features
- 🎨 Heatmap Visualization - Color-coded pulse density display
- 📊 Color Palette Legend - Visual scale for pulse counts
- 〰️ Sine Wave Overlay - Reference waveform display
- 🔍 Zoom & Pan - Interactive data exploration
- 📏 Y-Range Control - Adjustable amplitude range (100-5000 or custom)
- 🔄 Resolution Modes - UNI/BI and HI/LO toggle options
- 🪟 Windowing Support - Filter specific phase/amplitude regions
- 📸 Screenshot - Export chart as PNG image
- 📈 Statistics - Real-time peak value and total count
TrendsChart
The TrendsChartComponent displays multiple time series with interactive features.
Usage
import { Component } from '@angular/core';
import { TrendsChartComponent, TrendLineData } from '@rm-graph/angular';
@Component({
selector: 'app-dashboard',
standalone: true,
imports: [TrendsChartComponent],
template: `
<sui-trends-chart
id="my-trends-chart"
[data]="trendsData"
height="500px"
[showLegend]="true"
[showResetButton]="true"
[showPointMarkerToggle]="true"
[showOverview]="true"
/>
`
})
export class DashboardComponent {
trendsData: TrendLineData[] = [
{
name: 'Channel 1',
values: [150, 145, 160, 155, 148],
timestamps: [1707012000000, 1707012060000, 1707012120000, 1707012180000, 1707012240000],
color: '#3b82f6',
uom: 2,
},
{
name: 'Channel 2',
values: [100, 95, 110, 105, 98],
timestamps: [1707012000000, 1707012060000, 1707012120000, 1707012180000, 1707012240000],
color: '#ef4444',
uom: 2,
},
];
}TrendLineData Interface
| Property | Type | Required | Description |
|----------|------|----------|-------------|
| name | string | ✅ | Display name for the series (shown in legend) |
| values | number[] | ✅ | Array of numeric values (Y-axis data points) |
| timestamps | number[] | ✅ | Array of timestamps in milliseconds (X-axis data points) |
| color | string | ✅ | Line color (hex or CSS color, e.g., #3b82f6) |
| uom | number | ❌ | Optional unit of measurement code |
Note: The
valuesandtimestampsarrays must have the same length.
Inputs
| Input | Type | Default | Description |
|-------|------|---------|-------------|
| id | string | auto-generated | Unique ID for the chart |
| data | TrendLineData[] | [] | Array of trend line data |
| height | string | '400px' | Chart height |
| xAxisTitle | string | "Time (HH:MM)" | X-axis title |
| yAxisTitle | string | Based on chartType | Y-axis title |
| showLegend | boolean | true | Show interactive legend |
| showResetButton | boolean | true | Show reset zoom button |
| showPointMarkerToggle | boolean | true | Show point marker toggle |
| showOverview | boolean | true | Show overview mini-map |
Features
- 📊 Interactive Legend - Click to toggle series visibility
- 🔍 Zoom & Pan - Rubber band zoom, mouse wheel zoom, drag to pan
- 🔄 Reset Zoom - One-click reset to initial view
- 📍 Point Markers - Toggle visibility of data point markers
- 🗺️ Overview Chart - Mini-map for navigation
- 📸 Screenshot - Export chart as PNG with header
- 📅 Custom Date Labels - Formatted as DD/MM/YYYY HH:MM
Theming
import { getThemeManager } from '@rm-graph/core';
// Built-in themes: 'light', 'dark', 'modern', 'midnight'
// Register custom theme
getThemeManager().registerTheme({
name: 'custom',
backgroundColor: '#1a1a2e',
colorPalette: ['#ff6b6b', '#4ecdc4'],
// ...
});🔑 SciChart License Configuration
This package uses SciChart.js, which requires a valid license key to remove watermarks. The license system supports encrypted storage and multiple configuration methods.
Complete License Flow
Step 1: Get Your SciChart License Key
- Register your domain at SciChart License Portal
- Add allowed domains to your license:
- For production:
yourdomain.com,app.yourdomain.com - For development:
localhost,127.0.0.1, or your local IP (e.g.,192.168.140.16)
- For production:
- Copy your license key (format:
xxxx-xxxx-xxxx-xxxx)
Important: SciChart licenses are domain-locked. Make sure to register all domains where you'll use the charts.
Step 2: Configure License in Your App
You have three options to configure the license:
Option A: Using LicenseManager Component (Recommended for User-Facing Apps)
Add the LicenseManagerComponent to your settings page:
import { Component } from '@angular/core';
import { LicenseManagerComponent } from '@rm-graph/angular';
import type { LicenseStatus } from '@rm-graph/angular';
@Component({
selector: 'app-settings',
standalone: true,
imports: [LicenseManagerComponent],
template: `
<rm-license-manager
title="SciChart License Configuration"
placeholder="Paste your SciChart license key here"
theme="light"
(licenseChange)="onLicenseChange($event)"
></rm-license-manager>
`
})
export class SettingsComponent {
onLicenseChange(status: LicenseStatus) {
if (status.isApplied) {
console.log('License activated!');
// Optionally reload to apply to existing charts
window.location.reload();
}
}
}User Flow:
- User opens settings page
- User enters SciChart license key in the input field
- User clicks "Save License"
- License is encrypted and stored in localStorage
- License is immediately applied to SciChart
- User may need to reload page for existing charts to pick up the license
Option B: Programmatic Setup (For Admin/Backend Configuration)
// In your app initialization (main.ts or app.component.ts)
import { loadLicense, setLicense } from '@rm-graph/angular';
import { bootstrapApplication } from '@angular/platform-browser';
import { AppComponent } from './app/app.component';
// Load existing license on app startup
async function initializeApp() {
const loaded = await loadLicense();
if (loaded) {
console.log('License loaded from storage');
} else {
console.log('No license found - charts will show watermark');
}
// Bootstrap your app
bootstrapApplication(AppComponent).catch(err => console.error(err));
}
initializeApp();
// To set a license programmatically:
async function configureLicense(licenseKey: string) {
const success = await setLicense(licenseKey);
if (success) {
console.log('License saved and applied!');
}
}Option C: Environment Variable (For Build-Time Configuration)
# .env file or environment configuration
SCICHART_LICENSE_KEY=your-license-key-here// main.ts
import { setupSciChartLicense } from '@rm-graph/angular';
// This will automatically read from SCICHART_LICENSE_KEY
setupSciChartLicense({
fromEnv: true,
fromLocalStorage: true, // Also check encrypted storage
});
bootstrapApplication(AppComponent).catch(err => console.error(err));Step 3: License Storage & Security
The license system provides encrypted storage for security:
- Encryption: License keys are encrypted using AES-256-GCM (when Web Crypto API is available) or base64 encoding (fallback)
- Storage: Encrypted license is stored in
localStorageunder keyRMGRAPH_LICENSE_DATA - Security Note: While encrypted, this provides obfuscation rather than true security since decryption happens client-side. For production, combine with SciChart's domain-locking.
Storage Flow:
User enters license → Encrypt → Store in localStorage → Decrypt on load → Apply to SciChartStep 4: Verify License is Working
After configuring the license:
- Check browser console - Should see no license errors
- Check charts - Should render without "SCICHART" watermark
- Check license status:
import { getLicenseStatus, hasStoredLicense, isLicenseApplied } from '@rm-graph/angular';
const status = getLicenseStatus();
console.log('Has license stored:', status.hasLicense);
console.log('License applied:', status.isApplied);
// Or use individual functions
if (hasStoredLicense()) {
console.log('License exists in storage');
}
if (isLicenseApplied()) {
console.log('License is active');
}LicenseManagerComponent API
Selector: <rm-license-manager>
| Input | Type | Default | Description |
|-------|------|---------|-------------|
| title | string | "SciChart License Configuration" | Title text |
| placeholder | string | "Paste your SciChart license key here" | Input placeholder |
| successMessageDuration | number | 2000 | Success message display duration (ms) |
| autoLoad | boolean | true | Auto-load license on mount |
| requireReload | boolean | true | Show reload notice after license change |
| theme | 'light' \| 'dark' | 'light' | Component theme |
| className | string | - | Custom CSS class name |
| Output | Type | Description |
|--------|------|-------------|
| licenseChange | EventEmitter<LicenseStatus> | Emitted when license status changes |
License Management Functions
import {
// Configuration
configureLicenseEncryption,
// License operations
setLicense, // Save and apply license
loadLicense, // Load from storage and apply
removeLicense, // Remove stored license
applyLicenseKey, // Apply directly without storing
// Status checks
hasStoredLicense, // Check if license exists in storage
isLicenseApplied, // Check if license is active
getLicenseStatus, // Get full status object
validateLicenseFormat, // Validate key format
// Types
type LicenseStatus,
type LicenseConfig,
} from '@rm-graph/angular';Complete Example: Full License Setup
// main.ts
import { bootstrapApplication } from '@angular/platform-browser';
import { AppComponent } from './app/app.component';
import { loadLicense } from '@rm-graph/angular';
async function init() {
await loadLicense();
bootstrapApplication(AppComponent).catch(err => console.error(err));
}
init();// app.component.ts
import { Component } from '@angular/core';
import { CommonModule } from '@angular/common';
import { LicenseManagerComponent, Column3DChartComponent } from '@rm-graph/angular';
import type { LicenseStatus } from '@rm-graph/angular';
@Component({
selector: 'app-root',
standalone: true,
imports: [CommonModule, LicenseManagerComponent, Column3DChartComponent],
template: `
<header>
<h1>My Chart App</h1>
<button (click)="toggleLicenseManager()">
🔑 License Settings
</button>
</header>
<rm-license-manager
*ngIf="showLicenseManager"
title="SciChart License Configuration"
placeholder="Paste your SciChart license key here"
theme="light"
(licenseChange)="onLicenseChange($event)"
></rm-license-manager>
<rm-column3d-chart
[data]="chartData"
height="400px"
></rm-column3d-chart>
`
})
export class AppComponent {
showLicenseManager = false;
chartData = [[45, 1500, 1], [90, 2200, 2]];
toggleLicenseManager() {
this.showLicenseManager = !this.showLicenseManager;
}
onLicenseChange(status: LicenseStatus) {
console.log('License status:', status);
// Don't auto-close - let user see success message and click Reload
}
}Troubleshooting
Issue: "License key is not valid for this domain"
Cause: Your license key is registered for a different domain than where you're accessing the app.
Solutions:
- Check your registered domains in SciChart portal
- Add the current domain to your license:
- For localhost: Add
localhostand127.0.0.1 - For IP access: Add your IP (e.g.,
192.168.140.16) - For production: Add your production domain
- For localhost: Add
- Access via registered domain: Use the exact domain/IP you registered
Issue: "Failed to save license" / Crypto errors
Cause: Web Crypto API is not available (requires HTTPS or localhost).
Solutions:
- Use HTTPS: Deploy with SSL certificate
- Use localhost: Access via
http://localhost:PORTinstead of IP - The system will automatically fallback to base64 encoding (less secure but functional)
Issue: Charts still show watermark after setting license
Solutions:
- Reload the page - Existing charts need to be recreated with the license
- Check license status:
getLicenseStatus()to verify it's applied - Check browser console for license errors
- Verify domain matches your registered license
Issue: License works on localhost but not on IP
Cause: SciChart licenses are domain-specific.
Solutions:
- Register both domains: Add both
localhostAND your IP to the license - Use consistent access method: Always use the same URL format
- For development: Consider using
localhostonly
Security Best Practices
- Domain Locking: Always register specific domains in SciChart portal
- Environment Variables: Use environment configuration for build-time setup (never commit keys)
- HTTPS: Use HTTPS in production for secure crypto operations
- User Input: If allowing users to enter licenses, validate format before saving
- Storage: Encrypted storage provides obfuscation, not true security (decryption is client-side)
License Status Types
interface LicenseStatus {
/** Whether a license is stored in localStorage */
hasLicense: boolean;
/** Whether the license was successfully applied to SciChart */
isApplied: boolean;
/** Error message if license operation failed */
error?: string;
}See main README for full documentation.
