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 raceEntities: any = [
'United States',
'China',
'Germany',
'Japan',
'France',
'United Kingdom',
'India',
'Brazil',
'Canada',
'Italy',
];
private raceColors: any = {
'United States': '#3B82F6',
China: '#EF4444',
Germany: '#F59E0B',
Japan: '#10B981',
France: '#8B5CF6',
'United Kingdom': '#EC4899',
India: '#14B8A6',
Brazil: '#F97316',
Canada: '#6366F1',
Italy: '#84CC16',
};
private raceFlags: any = {
'United States': '🇺🇸',
China: '🇨🇳',
Germany: '🇩🇪',
Japan: '🇯🇵',
France: '🇫🇷',
'United Kingdom': '🇬🇧',
India: '🇮🇳',
Brazil: '🇧🇷',
Canada: '🇨🇦',
Italy: '🇮🇹',
};
private raceValues: any = {};
private raceFrame = (): any => {
this.raceEntities.forEach(function (name) {
this.raceValues[name] = Math.max(
50,
this.raceValues[name] + (Math.random() - 0.45) * 220,
)
})
var ranked = this.raceEntities
.map(function (name) {
return { name: name, value: Math.round(this.raceValues[name]) }
})
.sort(function (a, b) {
return b.value - a.value
})
return {
categories: ranked.map(function (r) {
return r.name
}),
data: ranked.map(function (r) {
return r.value
}),
// Colors follow the ranking so each country keeps its own color (see above).
colors: ranked.map(function (r) {
return this.raceColors[r.name]
}),
}
};
private raceInitial: any = this.raceFrame();
private raceRuns: any = 0;
private raceInterval: any;
public chartOptions: Partial<ChartOptions> = {
series: [
{
name: 'Value',
data: this.raceInitial.data,
},
],
chart: {
id: 'horizontal-bar-race',
type: 'bar',
height: 420,
animations: {
dynamicAnimation: {
speed: 800,
},
},
toolbar: {
show: false,
},
},
plotOptions: {
bar: {
borderRadius: 4,
borderRadiusApplication: 'end',
horizontal: true,
// Each bar gets its own color, taken from the `colors` palette by position.
distributed: true,
},
},
colors: this.raceInitial.colors,
dataLabels: {
enabled: true,
// Opt-in race polish: the value labels ride to each bar's new rank and
// count up/down from their previous value on every tick.
animate: {
enabled: true,
},
countUp: {
enabled: true,
},
},
title: {
text: 'Bar Chart Race',
align: 'left',
},
xaxis: {
categories: this.raceInitial.categories,
},
yaxis: {
labels: {
// Prepend each country's flag emoji to its axis label.
formatter: (val) => {
return (this.raceFlags[val] || '') + ' ' + val
},
},
},
legend: {
show: false,
},
};
ngAfterViewInit() {
(window as any).ApexCharts.setLicense('APEX-eyJpc3N1ZURhdGUiOiIyMDI2LTA0LTE2IiwiZXhwaXJ5RGF0ZSI6IjIwNTItMDQtMTciLCJwbGFuIjoiZW50ZXJwcmlzZSIsImRvbWFpbnMiOlsiYXBleGNoYXJ0cy5jb20iLCIxMjcuMC4wLjEiXX0=');
this.raceEntities.forEach(function (name, i) {
this.raceValues[name] = 300 + (this.raceEntities.length - i) * 90
})
this.raceInterval = window.setInterval(() => {
this.raceRuns++
var frame = this.raceFrame()
this.chart.updateOptions({
series: [{ data: frame.data }],
xaxis: { categories: frame.categories },
colors: frame.colors,
})
if (this.raceRuns === 2 && (window as any).isATest === true) {
clearInterval(this.raceInterval)
}
}, 1200)
}
ngOnDestroy() {
if (this.raceInterval) clearInterval(this.raceInterval);
}
}