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 bulletData: any = [
          { x: 'Revenue', y: 78, target: 85, ranges: [50, 75, 100] },
          { x: 'Profit', y: 62, target: 70, ranges: [40, 65, 90] },
          { x: 'Cust. Sat', y: 88, target: 80, ranges: [60, 80, 95] },
          { x: 'Retention', y: 71, target: 75, ranges: [55, 70, 90] },
          { x: 'On-time', y: 94, target: 90, ranges: [70, 85, 100] },
        ];

  public chartOptions: Partial<ChartOptions> = {
          series: [{ name: 'Performance', data: this.bulletData }],
          chart: {
            height: 440,
            type: 'bullet',
            animations: { enabled: false },
            toolbar: { show: false },
          },
          grid: {
            padding: { left: 15, right: 15 },
          },
          title: {
            text: 'Q2 KPIs vs target (percent of goal)',
            align: 'left',
          },
          subtitle: {
            text: 'A custom "bullet" series type built with Marks; yExtent drives auto-scaling',
            align: 'left',
          },
          xaxis: {
            type: 'category',
            tickPlacement: 'between',
            tooltip: { enabled: false },
          },
          yaxis: {
            min: 0,
            max: 100,
            tickAmount: 5,
            labels: {
              formatter: (val) => {
                return Math.round(val) + '%'
              },
            },
          },
          tooltip: {
            // The default tooltip would show the yExtent range (0% - 100%), which is not
            // meaningful for a bullet. Show the datum's actual value vs its target.
            custom: (opts) => {
              var d = opts.w.config.series[opts.seriesIndex].data[opts.dataPointIndex]
              var hit = d.y >= d.target
              return (
                '<div style="padding:7px 11px;font-family:sans-serif;font-size:12px;line-height:1.5">' +
                '<div style="font-weight:600;margin-bottom:3px">' +
                d.x +
                '</div>' +
                'Actual: <b>' +
                d.y +
                '%</b><br>' +
                'Target: <b>' +
                d.target +
                '%</b> ' +
                '<span style="color:' +
                (hit ? '#1f9d57' : '#e53e3e') +
                '">' +
                (hit ? '(met)' : '(below)') +
                '</span>' +
                '</div>'
              )
            },
          },
        };
  ngAfterViewInit() {
    (window as any).ApexCharts.setLicense('APEX-eyJleHBpcnlEYXRlIjoiMjEyNi0wNy0wNCIsImlzc3VlRGF0ZSI6IjIwMjYtMDctMjgiLCJwbGFuIjoicHJlbWl1bSIsImRvbWFpbnMiOlsiYXBleGNoYXJ0cy5jb20iLCIxMjcuMC4wLjEiLCJsb2NhbGhvc3QiXSwic2lnIjoieVBmb1VCc0Z3TU9ZdUEyaEZkR0I2Y1FtZ0JITUtXcVdJSjB2NVRESXRZbFR3eDJMUmh6R2x0RUc3VXJ4X0s3b25ZMWRZb2Z2VGItN01ydFYyNDVyOWcifQ==');
    ApexCharts.registerSeriesType('bullet', {
            yExtent: (datum) => {
              return [0, Math.max(datum.target, datum.ranges[datum.ranges.length - 1])]
            },
            renderItem: (ctx) => {
              var datum = ctx.datum
              var scales = ctx.scales
              var api = ctx.api

              // Bullets are bar-like. With xaxis.tickPlacement:'between' the axis is a
              // band axis, so ctx.x is the center of this datum's band and scales.band
              // is the band width. Sizing the bullet under the band keeps it inside the
              // plot and aligned with its label.
              var cx = ctx.x
              var half = (scales.band * 0.62) / 2

              // qualitative range bands, light to dark, stacked from the baseline up
              var shades = ['#e8eef5', '#cdd8e6', '#aebfd4']
              var prev = 0
              for (var r = 0; r < datum.ranges.length; r++) {
                var top = scales.y(datum.ranges[r])
                var bottom = scales.y(prev)
                api.rect({
                  x: cx - half,
                  y: top,
                  w: half * 2,
                  h: bottom - top,
                  fill: shades[r],
                })
                prev = datum.ranges[r]
              }

              // measured value: a narrow dark bar from the baseline up to y
              var yVal = scales.y(datum.y)
              var mHalf = half * 0.38
              api.rect({
                x: cx - mHalf,
                y: yVal,
                w: mHalf * 2,
                h: scales.y(0) - yVal,
                fill: '#2d3748',
              })

              // target: a red tick across the band
              var yTarget = scales.y(datum.target)
              api.line({
                x1: cx - half,
                y1: yTarget,
                x2: cx + half,
                y2: yTarget,
                stroke: '#e53e3e',
                width: 3,
              })
            },
          })
  }

  ngOnDestroy() {
    // no cleanup needed
  }
}
Bullet Chart - Angular Custom Series | ApexCharts.js | ApexCharts.js