Stacked Columns and Bars with One Traffic Source Highlighted in Angular
Using ApexCharts with Angular
This Stacked Columns and Bars with One Traffic Source Highlighted 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>.
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';
import 'apexcharts/features/highlight-filter';
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 MONTHS: any = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun'];
private DEVICES: any = ['Desktop', 'Mobile'];
private SESSIONS: any = [
[42, 45, 48, 44, 50, 53],
[58, 62, 66, 71, 75, 80],
];
private SOURCES: any = {
search: [
[20, 22, 23, 21, 24, 25],
[24, 26, 28, 30, 31, 33],
],
social: [
[8, 9, 10, 9, 11, 12],
[22, 24, 26, 29, 31, 34],
],
email: [
[9, 9, 10, 9, 10, 11],
[7, 7, 8, 8, 9, 9],
],
};
private seriesFor = (source: any): any => {
return this.DEVICES.map(function (name, i) {
var s = { name: name, data: this.SESSIONS[i] }
if (source) s.highlightData = this.SOURCES[source][i]
return s
})
};
private pickSource = (source: any): any => {
document.querySelectorAll('[data-source]').forEach(function (b) {
b.classList.toggle('active', b.getAttribute('data-source') === source)
})
return Promise.all(
['sessions', 'shares'].map(function (id) {
return ApexCharts.getChartByID(id).updateSeries(this.seriesFor(source))
}),
)
};
private wireSources = (): any => {
document.querySelectorAll('[data-source]').forEach(function (b) {
b.addEventListener('click', function () {
this.pickSource(b.getAttribute('data-source'))
})
})
};
public chartOptions: Partial<ChartOptions> = {
series: this.seriesFor('search'),
chart: {
id: 'sessions',
type: 'bar',
height: 360,
stacked: true,
toolbar: { show: false },
},
colors: ['#2563eb', '#16a34a'],
plotOptions: {
bar: {
columnWidth: '56%',
borderRadius: 4,
borderRadiusWhenStacked: 'last',
dataLabels: { total: { enabled: true, style: { fontSize: '12px' } } },
},
},
dataLabels: { enabled: false },
stroke: { show: true, width: 1, colors: ['#fff'] },
xaxis: { categories: this.MONTHS },
yaxis: { title: { text: 'Sessions (thousands)' } },
legend: { position: 'bottom' },
tooltip: { shared: true, intersect: false },
};
public chartOptions2: Partial<ChartOptions> = {
series: this.seriesFor('search'),
chart: {
id: 'shares',
type: 'bar',
height: 280,
stacked: true,
stackType: '100%',
toolbar: { show: false },
},
colors: ['#2563eb', '#16a34a'],
plotOptions: {
bar: { horizontal: true, barHeight: '64%' },
},
dataLabels: { enabled: true, style: { fontSize: '11px' } },
stroke: { show: true, width: 1, colors: ['#fff'] },
xaxis: { categories: this.MONTHS },
legend: { show: false },
tooltip: { shared: true, intersect: false },
};
ngAfterViewInit() {
(window as any).ApexCharts.setLicense('APEX-eyJleHBpcnlEYXRlIjoiMjEyNi0wNy0wNCIsImlzc3VlRGF0ZSI6IjIwMjYtMDctMjgiLCJwbGFuIjoicHJlbWl1bSIsImRvbWFpbnMiOlsiYXBleGNoYXJ0cy5jb20iLCIxMjcuMC4wLjEiLCJsb2NhbGhvc3QiXSwic2lnIjoieVBmb1VCc0Z3TU9ZdUEyaEZkR0I2Y1FtZ0JITUtXcVdJSjB2NVRESXRZbFR3eDJMUmh6R2x0RUc3VXJ4X0s3b25ZMWRZb2Z2VGItN01ydFYyNDVyOWcifQ==');
this.wireSources()
function pickSource(source) {
document.querySelectorAll('[data-source]').forEach(function (b) {
b.classList.toggle('active', b.getAttribute('data-source') === source)
})
return Promise.all(
['sessions', 'shares'].map(function (id) {
return ApexCharts.getChartByID(id).updateSeries(this.seriesFor(source))
}),
)
}
function wireSources() {
document.querySelectorAll('[data-source]').forEach(function (b) {
b.addEventListener('click', () => {
this.pickSource(b.getAttribute('data-source'))
})
})
}
}
ngOnDestroy() {
// no cleanup needed
}
}