Multiple bar charts side by side chartjs - chart.js

I'm trying to create a bar chart that is grouped by the dataset rather than the label in chartjs3 and I'm having no luck. Before I switch over to building in D3, I wanted to check if this achievable within the confines of ChartJS.
Here is a link to a fiddle where I've been playing around with using different dataset structures and options from the Bar chart docs. I suspect that this is outside of the scope of the charting library, or is within the scope, but requires some custom components to be made – I would appreciate if anyone could direct me on what in particular I would need to extend (e.g. is it a case of a custom axes? or more?).
https://jsfiddle.net/f34ucs76/2/
const data = {
labels: [
'2000','2010','2020',
// 'Frogs','Monkeys','Squirrels','Bats'
],
datasets: [
{
label: 'Frogs',
data: [30.2,20.8,36.2],
backgroundColor: 'red',
//stack: 'Frogs',
},
{
label: 'Monkeys',
data: [16.3,13.0,22.3],
backgroundColor: 'blue',
//stack: 'Monkeys',
},
{
label: 'Squirrels',
data: [5.8,3.1,14.9],
backgroundColor: 'cyan',
//stack:'Squirrels',
},
{
label: 'Bats',
data: [0.1,3.6,2.6],
backgroundColor: 'aquamarine',
//stack:'Bats',
},
],
}
const options = {
maintainAspectRatio: false,
responsive: true,
interaction: {
intersect: false,
mode: 'index',
},
plugins: {
title: {
display: false,
},
subtitle: {
display: true,
align: 'start',
text: ['Source: Animals Index, June 2022'],
font: {
size: 12,
style: 'italic',
},
position: 'bottom',
padding: {
top: 20,
},
},
legend: {
display: true,
position: 'bottom',
},
tooltip: {
enabled: true,
},
},
scales: {
x: {
grid: {
display: false,
},
},
y: {
title: {
display: true,
text: 'Horsepower',
},
},
},
}
const mychart = new Chart(
document.getElementById('mychart'),
{
type: 'bar',
data: data,
options: options,
}
);

Your problem can be solved with Chart.js but the labels and the single dataset need to be generated.
Further you need to define your own legend by defining a plugins.legend.labels.generateLabels together with with a plugins.legend.labels.onClick function.
For further information, consult the Legend page from the Chart.js. documentation.
Please take a look at your amended and runnable code below and see how it works.
const baseData = {
labels: ['2000','2010','2020'],
datasets: [
{ label: 'Frogs', data: [30.2, 20.8, 36.2], bgColor: 'red', hidden: false },
{ label: 'Monkeys', data: [16.3, 13.0, 22.3], bgColor: 'blue', hidden: false },
{ label: 'Squirrels', data: [5.8,3.1, 14.9], bgColor: 'cyan', hidden: false },
{ label: 'Bats', data: [0.1, 3.6, 2.6], bgColor: 'aquamarine', hidden: false }
]
};
const iLastDs = baseData.datasets.length - 1;
const data = {
labels: baseData.datasets
.map(() => baseData.labels)
.map((labels, i) => i < iLastDs ? [...labels, null] : labels)
.flatMap(v => v),
datasets: [{
data: baseData.datasets
.map(ds => ds.data)
.map((data, i) => i < iLastDs ? [...data, null] : data)
.flatMap(v => v),
backgroundColor: baseData.datasets
.map(ds => ds.data.map(v => ds.bgColor))
.map((bgColors, i) => i < iLastDs ? [...bgColors, null] : bgColors)
.flatMap(v => v),
categoryPercentage: 1,
barPercentage: 0.9
}]
};
const options = {
maintainAspectRatio: true,
responsive: true,
interaction: {
intersect: true,
mode: 'index',
},
plugins: {
legend: {
position: 'bottom',
labels: {
generateLabels: chart => baseData.datasets.map((ds, i) => ({
datasetIndex: i,
text: ds.label,
fillStyle: ds.bgColor,
strokeStyle: 'lightgray',
hidden: baseData.datasets[i].hidden
}))
},
onClick: (event, legendItem, legend) => {
baseData.datasets[legendItem.datasetIndex].hidden = !baseData.datasets[legendItem.datasetIndex].hidden;
const iFirstValue = legendItem.datasetIndex + legendItem.datasetIndex * baseData.labels.length;
for (let i = iFirstValue; i < iFirstValue + baseData.labels.length; i++) {
legend.chart.toggleDataVisibility(i);
}
legend.chart.update();
}
},
tooltip: {
callbacks: {
title: ctx => {
const dsIndex = Math.floor(ctx[0].dataIndex / baseData.datasets.length);
return baseData.datasets[dsIndex].label + ' / ' + ctx[0].label;
}
}
}
},
scales: {
x: {
grid: {
display: false
}
},
y: {
title: {
display: true,
text: 'Horsepower',
}
}
}
};
new Chart('mychart', {
type: 'bar',
data,
options
});
span {
font-style: italic;
font-size: 12px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/3.9.1/chart.min.js"></script>
<canvas id="mychart" height="100"></canvas>
<span>Source: Animals Index, June 2022</span>

Related

GBP currency conversion of Apex Charts data labels

I'm learning to work with Apexcharts and need to show the labels (Y axis labels an data labels) as currency (GBP) but can't seem to find the right approach. I have looked at the locale code, but really not sure how to integrate this into my chart.
I'm trying to ensure that 1000 become 1,000 etc.
Below is the chart code
<script>
var options = {
series: [
{
name: "Price in £s",
data: [1000, 2000, 2500, 3000, 4800, 6000]
}
],
chart: {
height: 350,
type: 'bar',
id: 'areachart-2',
dropShadow: {
enabled: true,
color: '#000',
top: 18,
left: 7,
blur: 10,
opacity: 0.2
},
toolbar: {
show: false
}
},
colors: ['#f6564d', '#545454'],
dataLabels: {
enabled: false,
textAnchor: 'middle',
formatter: function(val, index) {
return val.toFixed(0);
}
},
stroke: {
curve: 'straight'
},
title: {
text: '',
align: 'left'
},
grid: {
borderColor: '#f1f1f1',
row: {
colors: ['#fff', 'transparent'], // takes an array which will be repeated on columns
opacity: 0.5
},
},
markers: {
size: 100
},
xaxis: {
categories: ['01/05/22','01/06/22','01/07/22','01/08/22','01/09/22','01/10/22'],
title: {
text: 'Month'
}
},
yaxis: {
min:00,
title: {
text: 'Price in GBP',
},
},
legend: {
position: 'top',
horizontalAlign: 'right',
floating: true,
offsetY: 100,
offsetX: 100
}
};
var chart = new ApexCharts(document.querySelector("#chart"), options);
chart.render();
</script>
In yaxis add labels formatter
labels:{
formatter: (value) => {
return `${numberWithCommas(value)} GBP`;
},
},
And use this regex to add commas
function numberWithCommas(x) {
return x.toString().replace(/\B(?<!\.\d*)(?=(\d{3})+(?!\d))/g, ",");
}
Axes labels formatting can be controlled by yaxis.labels.formatter and xaxis.labels.formatter.
yaxis: {
labels: {
formatter: function (value) {
return value + "$";
}
},
},
xaxis: {
labels: {
formatter: function (value) {
return value;
}
}
}

Add labels to bar chart: chartjs

I am creating bar charts using chartjs 3.5.1, and I am new to chartjs. I want to add value labels to each bar in my bar chart.
I have reviewed some relevant answers, but they are either too old or too complicated to achieve.
Below is the intended outcome, note that the non-hand-written part is what I have achieved.
The code is below:
let chart2 = new Chart('chart2', {
type: 'bar',
data: {
labels: ["a", "b", "c", "d"],
datasets: [{
data: [500, 400, 300, 200],
}]
},
options: {
indexAxis: 'y',
plugins: {
title: {
display: true,
text: "Graph"
},
legend: {
display: false,
}
},
scales: {
y: {
grid: {
display: false
},
},
x: {
grid: {
display: false
}
}
}
},
});
Really appreciate your help!
You can use the chartjs-plugin-datalabels library.
First you'll have to register the plugin, then you can define the desired options inside options.plugins.datalabels.
Please take a look at your amended code and see how it works.
Chart.register(ChartDataLabels);
new Chart('chart2', {
type: 'bar',
data: {
labels: ["a", "b", "c", "d"],
datasets: [{
data: [500, 400, 300, 200],
}]
},
options: {
indexAxis: 'y',
layout: {
padding: {
right: 60
}
},
plugins: {
title: {
display: true,
text: "Graph"
},
legend: {
display: false,
},
datalabels: {
color: 'blue',
anchor: 'end',
align: 'right',
labels: {
title: {
font: {
weight: 'bold'
}
}
}
}
},
scales: {
y: {
grid: {
display: false
},
},
x: {
grid: {
display: false
}
}
}
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/3.5.1/chart.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/chartjs-plugin-datalabels/2.0.0/chartjs-plugin-datalabels.min.js"></script>
<canvas id="chart2" height="100"></canvas>

How can I change the color of certain lines in chartjs / vue-chartjs?

I notice that a default bar chart always has a darker zero line. I also notice the horizontal line at -1.0 is also darker. I want those colors to be the same as the grid. How do I do that?
A bare-bones example is shown here:
https://jsfiddle.net/Lnv3cmz0/1/
Vue.component('chart', {
template:`
<canvas></canvas>
`,
props:['type'],
mounted(){
this._chart = new Chart(this.$el, {
type:this.type,
data:[],
})
},
})
//App setup
new Vue({
el:'#vue',
})
Update:
The zeroLine option no longer seems to be available. I am using the latest/greatest Chart (v4.2.0) and no longer have this issue. Below is a sample of what I'm doing
const ctx = document.getElementById('chart') as HTMLCanvasElement;
chart.value = new Chart(ctx, {
type: 'bar',
data: {
datasets: [],
},
options: {
onClick: (evt) => handleOnClick(evt),
interaction: {
mode: 'nearest',
},
plugins: {
legend: {
labels: {
generateLabels(chart) { // other code here }}
}
},
title: {
display: true,
text: 'My Super cool Chart',
},
},
scales: {
x: {
grid: {
color: () => myBackgroundColor,
},
beginAtZero: false,
ticks: {
callback: (value) => doSomethingOnCallaback(value),
// fontColor: myBackgroundColor,
},
type: 'linear',
},
y: {
grid: {
color: () => myBackgroundColor,
},
max: myStart,
min: myEnd,
beginAtZero: false,
ticks: {
callback: (value) => `Tick ${value}`,
stepSize: 15.0,
},
},
},
},
});
The answer is zeroLineColor in the GridLines
https://www.chartjs.org/docs/latest/axes/styling.html#grid-line-configuration

How to show different product name for every bar in chart js

I am using chart js for showing top 3 sold products in last week.
I want to show product name in tooltip for every bar which has obviously different products.
Here is my code :
function productChart() {
var data = {
labels: productHistoryDates,
datasets: [{
label: 'Product 1',
data: productHistoryProducts1,
backgroundColor: "#26B99A"
},
{
label: 'Product 2',
data: productHistoryProducts2,
backgroundColor: "#03586A",
},
{
label: 'Product 3',
data: productHistoryProducts3,
backgroundColor: "#03226A",
}]
};
var ctx = document.getElementById("productChart").getContext("2d");
var myBarChart = new Chart(ctx, {
type: 'bar',
data: data,
options: {
tooltips: {
enabled: true,
mode: 'single',
},
scales: {
yAxes: [{
gridLines: {
display: true
}
}],
xAxes: [{
gridLines: {
display: true
},
barPercentage: 0.8
}]
}
}
});
}
I am getting output like this.
But instead of product 1 label I want product name for that particular bar how can I achieve that ?
I solved it.
by passing an array to the label in data. And rest of the code is as below.
function productChart() {
var data = {
labels: productHistoryDates,
datasets: [{
label: productHistoryProducts1.name,
data: productHistoryProducts1.quantity,
backgroundColor: "#26B99A"
},
{
label: productHistoryProducts2.name,
data: productHistoryProducts2.quantity,
backgroundColor: "#03586A",
},
{
label: productHistoryProducts3.name,
data: productHistoryProducts3.quantity,
backgroundColor: "#03226A",
}]
};
var ctx = document.getElementById("productChart").getContext("2d");
var myBarChart = new Chart(ctx, {
type: 'bar',
data: data,
options: {
tooltips: {
enabled: true,
mode: 'single',
callbacks: {
label: function (tooltipItems, data) {
var tooltip = data.datasets[tooltipItems.datasetIndex].label[tooltipItems.index];
return tooltip;
}
}
},
scales: {
yAxes: [{
gridLines: {
display: true
}
}],
xAxes: [{
gridLines: {
display: true
},
ticks: { mirror: true },
barPercentage: 0.8
}]
}
}
});
}

Inline plugin doesn't work

According to the Chart.js docs, the following code should work:
new Chart(document.getElementById(chartID), {
type: 'pie',
responsive: true,
maintainAspectRatio: false,
data: {
labels: graph.globals.labels,
datasets: [{
backgroundColor: pieOptions.backgrounds,
data: pieOptions.objToArr(graph.meetingsData),
hoverBorderWidth: 5,
borderColor: 'transparent',
}]
},
options: {
title: {
display: true,
text: graph.globals.title,
},
legend: {
display: true,
position: 'bottom',
fullWidth: false,
onClick: () => {},
labels: {
generateLabels: (chart) => {
return pieOptions.legendLeft(chart);
}
}
},
plugins: [{
beforeInit: function(chart, options) {
console.log('yolo');
}
}]
rotation: 3.9,
}
});
However, when I create the plugin using a variable, or create it inline like shown above, it doesn't log anything. The only way that I can use plugin, is when I register it globally.
Can somebody explain to me why it doesn't work?
plugins should be placed in its own config section, and not nested under options.
Instead of:
options: {
title: {
display: true,
text: graph.globals.title,
},
legend: {
display: true,
position: 'bottom',
fullWidth: false,
onClick: () => {},
labels: {
generateLabels: (chart) => {
return pieOptions.legendLeft(chart);
}
}
},
plugins: [{
beforeInit: function(chart, options) {
console.log('yolo');
}
}]
rotation: 3.9,
}
Your code should look like:
options: {
title: {
display: true,
text: graph.globals.title,
},
legend: {
display: true,
position: 'bottom',
fullWidth: false,
onClick: () => {},
labels: {
// generateLabels: (chart) => {
// return pieOptions.legendLeft(chart);
// }
}
},
rotation: 3.9,
},
plugins: [{
beforeInit: function(chart, options) {
console.log('yolo');
}
}]
Working JSFiddle: https://jsfiddle.net/r1x63b8v/