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
  }
}
Continuous Datetime - Angular Heatmap Charts | ApexCharts.js | ApexCharts.js