Crowded Currency Labels in Angular

Using ApexCharts with Angular

This Crowded Currency Labels example wires ApexCharts into Angular through the ng-apexcharts component.

Install it with npm install ng-apexcharts apexcharts, then mount the chart with <apx-chart [series]="series" [chart]="chart"></apx-chart>.

Angular charts guide

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;
  public chartOptions: Partial<ChartOptions> = {
          series: [
            {
              name: 'Revenue',
              data: [
                {
                  x: '2023-Q3',
                  y: 168500000,
                },
                {
                  x: '2023-Q4',
                  y: 489400000,
                },
                {
                  x: '2024-Q1',
                  y: 1900000000,
                },
                {
                  x: '2024-Q2',
                  y: -811500000,
                },
                {
                  x: '2024-Q3',
                  y: 389500000,
                },
                {
                  x: '2024-Q4',
                  y: -703000000,
                },
                {
                  x: '2025-Q1',
                  y: 358000000,
                },
                {
                  x: '2025-Q2',
                  y: 138300000,
                },
                {
                  x: '2025-Q3',
                  y: -312700000,
                },
                {
                  x: '2025-Q4',
                  y: -346800000,
                },
                {
                  x: '2026-Q1',
                  y: 390300000,
                },
                {
                  x: '2026-Q2',
                  y: 109700000,
                },
                {
                  x: '2026-Q3',
                  y: -8400000,
                },
                {
                  x: '2026-Q4',
                  y: 14600000,
                },
                {
                  x: 'Latest',
                  isTotal: true,
                },
              ],
            },
          ],
          chart: {
            type: 'waterfall',
            height: 440,
          },
          title: {
            text: 'What moved revenue, quarter by quarter',
            align: 'left',
          },
          subtitle: {
            text: 'Currency labels are wider than the quarters they sit on. dataLabels.avoidOverlap spaces them apart.',
            align: 'left',
          },
          colors: ['#26a69a', '#ef5350', '#42a5f5'],
          dataLabels: {
            style: {
              fontSize: '11px',
            },
            formatter: (val) => {
              var abs = Math.abs(val)
              var sign = val < 0 ? '-' : '+'
              if (abs >= 1000000000)
                return sign + '$' + (abs / 1000000000).toFixed(2) + 'B'
              return sign + '$' + (abs / 1000000).toFixed(1) + 'M'
            },
          },
          xaxis: {
            labels: {
              rotate: -45,
              rotateAlways: true,
            },
          },
          yaxis: {
            labels: {
              formatter: (val) => {
                return '$' + (val / 1000000000).toFixed(1) + 'B'
              },
            },
          },
          tooltip: {
            y: {
              formatter: (val) => {
                return (val > 0 ? '+' : '') + '$' + val.toLocaleString('en-US')
              },
            },
          },
        };
  ngAfterViewInit() {
    (window as any).ApexCharts.setLicense('APEX-eyJleHBpcnlEYXRlIjoiMjEyNi0wNy0wNCIsImlzc3VlRGF0ZSI6IjIwMjYtMDctMjgiLCJwbGFuIjoicHJlbWl1bSIsImRvbWFpbnMiOlsiYXBleGNoYXJ0cy5jb20iLCIxMjcuMC4wLjEiLCJsb2NhbGhvc3QiXSwic2lnIjoieVBmb1VCc0Z3TU9ZdUEyaEZkR0I2Y1FtZ0JITUtXcVdJSjB2NVRESXRZbFR3eDJMUmh6R2x0RUc3VXJ4X0s3b25ZMWRZb2Z2VGItN01ydFYyNDVyOWcifQ==');
  }

  ngOnDestroy() {
    // no cleanup needed
  }
}