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 REGIONS: any = [
{ name: 'Americas', color: '#22C55E' },
{ name: 'Europe', color: '#0EA5E9' },
{ name: 'Asia', color: '#F59E0B' },
{ name: 'Oceania', color: '#F43F5E' },
];
private DATA: any = {
Americas: [
['New York', 55000, 100, 18.9, 2000],
['San Francisco', 62000, 98, 4.7, 650],
['Toronto', 40000, 72, 6.4, 400],
['Sao Paulo', 12000, 40, 22.6, 480],
['Mexico City', 11000, 38, 22.0, 480],
],
Europe: [
['Zurich', 72000, 118, 1.4, 340],
['London', 45000, 85, 14.3, 1000],
['Paris', 38000, 80, 11.1, 850],
['Berlin', 36000, 70, 6.1, 180],
['Moscow', 18000, 45, 12.6, 550],
],
Asia: [
['Tokyo', 40000, 83, 37.4, 1600],
['Singapore', 55000, 95, 5.9, 470],
['Dubai', 45000, 70, 3.5, 130],
['Hong Kong', 42000, 90, 7.5, 380],
['Seoul', 34000, 78, 26.0, 780],
['Shanghai', 20000, 60, 27.0, 680],
['Mumbai', 8000, 35, 20.7, 310],
['Delhi', 7000, 32, 32.0, 370],
],
Oceania: [
['Sydney', 48000, 88, 5.3, 400],
['Melbourne', 44000, 82, 5.0, 250],
],
};
private seriesFor = (sizeBy: any): any => {
return this.REGIONS.map(function (r) {
return {
name: r.name,
data: this.DATA[r.name].map(function (c) {
return {
name: c[0],
x: c[1],
y: c[2],
z: sizeBy === 'gdp' ? c[4] : c[3],
}
}),
}
})
};
private VIEWS: any = {
population: {
caption:
'Sized by population, the megacities swell: Tokyo, Delhi, Shanghai and Sao Paulo dominate, even where incomes are low and living is cheap.',
},
gdp: {
caption:
'Sized by economic output, the money centres take over: New York, Tokyo, London and Paris lead, while populous but poorer cities shrink.',
},
};
private buttons: any = document.querySelectorAll('#size-nav button');
private caption: any = document.getElementById('caption');
private showSize = (key: any): any => {
this.buttons.forEach(function (b) {
b.classList.toggle('active', b.getAttribute('data-size') === key)
})
this.caption.textContent = this.VIEWS[key].caption
this.chart.updateSeries(this.seriesFor(key))
};
public chartOptions: Partial<ChartOptions> = {
series: this.seriesFor('population'),
chart: {
id: 'cityBubbles',
type: 'unit',
height: 520,
animations: {
enabled: true,
speed: 700,
},
},
colors: this.REGIONS.map(function (r) {
return r.color
}),
// Translucent fills so overlapping bubbles read through each other.
fill: {
opacity: 0.7,
},
legend: {
position: 'bottom',
},
plotOptions: {
unit: {
layout: 'scatter',
scatter: {
y: 'value',
xMin: 0,
yMin: 0,
xTitle: 'Average income (US$/yr)',
yTitle: 'Cost of living (NYC = 100)',
xFormatter: (v) => {
return '$' + Math.round(v / 1000) + 'k'
},
sizeRange: [7, 44],
},
tooltip: {
formatter: (o) => {
var d = (o && o.datum) || {}
if (!d.name) return ''
return (
d.name +
': $' +
Math.round(d.x / 1000) +
'k income, cost of living ' +
d.y
)
},
},
},
},
};
ngAfterViewInit() {
(window as any).ApexCharts.setLicense('APEX-eyJpc3N1ZURhdGUiOiIyMDI2LTA0LTE2IiwiZXhwaXJ5RGF0ZSI6IjIwNTItMDQtMTciLCJwbGFuIjoiZW50ZXJwcmlzZSIsImRvbWFpbnMiOlsiYXBleGNoYXJ0cy5jb20iLCIxMjcuMC4wLjEiXX0=');
function showSize(key) {
this.buttons.forEach(function (b) {
b.classList.toggle('active', b.getAttribute('data-size') === key)
})
this.caption.textContent = this.VIEWS[key].caption
this.chart.updateSeries(this.seriesFor(key))
}
this.buttons.forEach(function (btn) {
btn.addEventListener('click', () => {
this.showSize(btn.getAttribute('data-size'))
})
})
}
ngOnDestroy() {
// no cleanup needed
}
}