This demo uses imperative chart updates. The generated code is a faithful Angular translation: open it in CodeSandbox to run and tweak.
import { Component, AfterViewInit, OnDestroy, ViewChild } from '@angular/core';
import {
ChartComponent,
ApexAxisChartSeries,
ApexNonAxisChartSeries,
ApexChart,
ApexXAxis,
ApexYAxis,
ApexTitleSubtitle,
ApexDataLabels,
ApexStroke,
ApexFill,
ApexLegend,
ApexTooltip,
ApexMarkers,
ApexPlotOptions,
ApexResponsive,
ApexGrid,
ApexAnnotations,
ApexStates,
ApexTheme,
NgApexchartsModule,
} from 'ng-apexcharts';
export type ChartOptions = {
series?: ApexAxisChartSeries | ApexNonAxisChartSeries;
chart?: ApexChart;
xaxis?: ApexXAxis;
yaxis?: ApexYAxis | ApexYAxis[];
title?: ApexTitleSubtitle;
subtitle?: ApexTitleSubtitle;
dataLabels?: ApexDataLabels;
stroke?: ApexStroke;
fill?: ApexFill;
legend?: ApexLegend;
tooltip?: ApexTooltip;
markers?: ApexMarkers;
plotOptions?: ApexPlotOptions;
responsive?: ApexResponsive[];
grid?: ApexGrid;
annotations?: ApexAnnotations;
states?: ApexStates;
theme?: ApexTheme;
colors?: string[];
labels?: any;
};
@Component({
selector: 'app-chart',
standalone: true,
imports: [NgApexchartsModule],
templateUrl: './chart.component.html',
})
export class AppChart implements AfterViewInit, OnDestroy {
@ViewChild('chart') chart!: ChartComponent;
private HOUR: any = 60 * 60 * 1000;
private start: any = new Date('2024-03-04T00:00:00.000Z').getTime();
private totalHours: any = 72;
private makeRand = (seed: any): any => {
var s = seed
return function () {
s = (s * 1103515245 + 12345) & 0x7fffffff
return s / 0x7fffffff
}
};
private isOutage = (h: any): any => {
return h >= 30 && h < 40
};
private hostSeries = (name: any, seed: any, base: any, swing: any): any => {
var rand = this.makeRand(seed)
var data = []
for (var h = 0; h < this.totalHours; h++) {
if (this.isOutage(h)) continue
var hourOfDay = h % 24
var daily = (Math.cos(((hourOfDay - 14) / 24) * Math.PI * 2) + 1) / 2
var cpu = base + swing * daily + (rand() - 0.5) * 12
data.push({
x: this.start + h * this.HOUR,
y: Math.max(1, Math.min(99, Math.round(cpu))),
})
}
return { name: name, data: data }
};
private heatData: any = [
this.hostSeries('web-01', 7, 22, 52),
this.hostSeries('web-02', 19, 20, 56),
this.hostSeries('api-01', 42, 27, 46),
this.hostSeries('api-02', 88, 25, 49),
this.hostSeries('cache-01', 123, 14, 16),
// The database is the hot box: steady high load that saturates (red) at the
// afternoon peak.
this.hostSeries('db-01', 256, 66, 18),
];
public chartOptions: Partial<ChartOptions> = {
series: this.heatData,
chart: {
height: 220,
type: 'heatmap',
toolbar: { show: false },
},
dataLabels: {
enabled: false,
},
title: {
text: 'Server CPU utilization across the fleet (a metrics outage shows as a real gap)',
align: 'left',
},
plotOptions: {
heatmap: {
// Flat CPU-health buckets on a green (idle) to red (saturated) scale, so a
// cell's color reads as a load level straight off the legend.
enableShades: false,
colorScale: {
ranges: [
{ from: 0, to: 30, color: '#00A65A', name: '0-30%' },
{ from: 30, to: 50, color: '#7CB342', name: '30-50%' },
{ from: 50, to: 70, color: '#FBC02D', name: '50-70%' },
{ from: 70, to: 85, color: '#FB8C00', name: '70-85%' },
{ from: 85, to: 100, color: '#E53935', name: '85-100%' },
],
},
},
},
xaxis: {
type: 'datetime',
labels: {
datetimeUTC: true,
datetimeFormatter: {
year: 'yyyy',
month: "MMM 'yy",
day: 'MMM dd',
hour: 'HH:mm',
},
},
},
tooltip: {
x: { show: true, format: 'MMM dd HH:mm' },
y: {
formatter: (val) => {
return val + '%'
},
},
},
};
ngAfterViewInit() {
(window as any).ApexCharts.setLicense('APEX-eyJpc3N1ZURhdGUiOiIyMDI2LTA0LTE2IiwiZXhwaXJ5RGF0ZSI6IjIwNTItMDQtMTciLCJwbGFuIjoiZW50ZXJwcmlzZSIsImRvbWFpbnMiOlsiYXBleGNoYXJ0cy5jb20iLCIxMjcuMC4wLjEiXX0=');
}
ngOnDestroy() {
// no cleanup needed
}
}