How to use two Y axes in Chart.js v2? - javascript

I am trying to create a line chart with two datasets, each with its own Y scale / axis (one to the left, one to the right of the graph) using Chart.js.
This is my code (jsfiddle):
var canvas = document.getElementById('chart');
new Chart(canvas, {
type: 'line',
data: {
labels: [ '1', '2', '3', '4', '5' ],
datasets: [
{
label: 'A',
yAxesGroup: 'A',
data: [ 100, 96, 84, 76, 69 ]
},
{
label: 'B',
yAxesGroup: 'B',
data: [ 1, 1, 1, 1, 0 ]
}
]
},
options: {
yAxes: [
{
name: 'A',
type: 'linear',
position: 'left',
scalePositionLeft: true
},
{
name: 'B',
type: 'linear',
position: 'right',
scalePositionLeft: false,
min: 0,
max: 1
}
]
}
});
However, the second axis is not visible and the second dataset is still scaled exactly as the first (0 to 100 instead of 0 to 1). What do I need to change?

For ChartJs 2.x only a couple changes need to be made (it looks like you have tried to combine 2.x options with the multi-axes options from my fork?),
The yAxes field needs to be in a scales object
the yAxis is referenced by id not name.
For the scale steps/size you just need to wrap these options in a ticks object.
No need forscalePositionLeft this is covered by position
Example:
var canvas = document.getElementById('chart');
new Chart(canvas, {
type: 'line',
data: {
labels: ['1', '2', '3', '4', '5'],
datasets: [{
label: 'A',
yAxisID: 'A',
data: [100, 96, 84, 76, 69]
}, {
label: 'B',
yAxisID: 'B',
data: [1, 1, 1, 1, 0]
}]
},
options: {
scales: {
yAxes: [{
id: 'A',
type: 'linear',
position: 'left',
}, {
id: 'B',
type: 'linear',
position: 'right',
ticks: {
max: 1,
min: 0
}
}]
}
}
});
fiddle example

The accepted answer no longer works as of 3.5, and the cause is listed as part of the breaking changes for 3.X (See 3.x Migration Guide)
The updated code below changes the scales property, from scales: {yScales: [...]} to scales: {[id]: {[options]}} , and also adds fill: true, (Was changed at 3.X from defaulting to true) and tension: 0.4 (The example provided before does have smooth curves, and seems like it was an undocumented "breaking" change)
var canvas = document.getElementById('myChart');
new Chart(canvas, {
type: 'line',
data: {
labels: ['1', '2', '3', '4', '5'],
datasets: [{
label: 'A',
yAxisID: 'A',
data: [100, 96, 84, 76, 69],
fill: true,
tension: 0.4
}, {
label: 'B',
yAxisID: 'B',
data: [1, 1, 1, 1, 0],
fill: true,
tension: 0.4
}]
},
options: {
scales: {
A: {
type: 'linear',
position: 'left',
},
B: {
type: 'linear',
position: 'right',
ticks: {
max: 1,
min: 0
}
}
}
}
});

This solution is working in react.
// "chart.js": "^2.9.4"
// "react-chartjs-2": "^2.11.1"
const lineChartData = {
data: {
labels: ['1', '2', '3', '4', '5', '6'],
datasets: [
{
label: '# of Votes',
data: [12, 19, 3, 5, 2, 3],
fill: false,
backgroundColor: 'rgb(255, 99, 132)',
borderColor: 'rgba(255, 99, 132, 0.2)',
},
{
label: '# of Votes',
data: [19, 9, 2, 1, 1, 21],
fill: false,
backgroundColor: 'rgb(122, 99, 255)',
borderColor: 'rgba(102, 99, 255, 0.2)',
}
],
},
options: {
scales: {
yAxes: [
{
type: 'linear',
display: true,
position: 'left',
ticks: {
beginAtZero: true,
},
},
{
type: 'linear',
display: true,
position: 'left',
ticks: {
beginAtZero: true,
},
},
],
},
}
}
<Line
data={lineChartData.data}
options={lineChartData.options}
height={30}
width={80}
options={{ maintainAspectRatio: true }}
/>

Related

Chart.js does not scale with two yAxis

I struggle to let my second y-Axis scale to my second data array.
The green data should be scaled according to the right y-Axis (not working).
The red data is correctly scaled to the left y-Axis.
<div id="chartWrapper" class="card-content column is-two-thirds">
<canvas id="myChart" height="250px"></canvas>
<script>
const ctx = document.getElementById('myChart').getContext('2d');
const myChart = new Chart(ctx, {
type: 'line',
data: {
labels: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23],
datasets: [{
label: 'Temperatur in C°',
yAxisID: 'A',
borderColor: "red",
data: 0
},
{
label: 'Niederschlagsmenge in mm',
yAxisID: 'B',
borderColor: "blue",
data: 0
}]
},
options: {
scales: {
A: {
type: 'linear',
beginAtZero: false,
position: 'left',
display: true
},
B: {
type: 'linear',
beginAtZero: false,
position: 'right',
display: true
}
}
}
});
</script>
</div>
Don't be confused with the data being 0, I add the data dynamically.
Thank you very much for any help.
This is because you are using V2 of chart.js in which the scales need to be configured as arrays like so:
new Chart(ctx, {
type: 'line',
data: {},
options: {
scales: {
yAxes: [{
id: 'A'
}, {
id: 'B'
}]
}
}
})

ChartJS Annotation line on stacked bar chart

I cannot get the annotation line to work on a stacked Bar Chart.
Example JS Fiddle: https://jsfiddle.net/cledwyn/rd5q6Lsz/10/
I have the standard Annotation per https://www.chartjs.org/chartjs-plugin-annotation/latest/samples/charts/bar.html
const annotation2 = {
type: 'line',
scaleID: 'x',
borderWidth: 3,
borderColor: 'black',
value: 8,
label: {
rotation: 'auto',
position: 'start',
backgroundColor: 'black',
content: 'Line at x=Label 5',
enabled: true
}
};
but when I stack the bar charts
scales: {
xAxes: { stacked: true },
yAxes: { stacked: true }
},
then the annotation line just goes from one corner of the chart to the other.
When running your JSFiddle, you can see the following warning on the console.
"No scale found with id 'x' for annotation 'annotation2'"
Therefore, changing scales.xAxes to scales.x should solve part of your problem.
scales: {
x: { stacked: true },
y: { stacked: true }
}
You'll further have to add missing labels to data.labels and adjust annotation2.value.
Please take a look at your amended code below and see how it works.
const labels = ['Label 1', 'Label 2', 'Label 3', 'Label 4', 'Label 5'];
const data = {
labels: labels,
datasets: [{
label: 'one',
data: [14, 21, 4, 5, 7],
backgroundColor: 'rgb(255, 99, 132)'
}, {
label: 'two',
data: [1, 3, 2, 4, 5],
backgroundColor: 'rgb(255, 199, 132)'
}, {
label: 'three',
data: [7, 1, 9, 6, 7],
backgroundColor: 'rgb(255, 99, 32)'
}]
};
const annotation2 = {
type: 'line',
scaleID: 'x',
borderWidth: 3,
borderColor: 'black',
value: 4,
label: {
rotation: 'auto',
position: 'start',
backgroundColor: 'black',
content: 'Line at x=Label 5',
enabled: true
}
};
const config = {
type: 'bar',
data: data,
options: {
plugins: {
annotation: {
annotations: {
annotation2,
}
}
},
scales: {
x: {
stacked: true
},
y: {
stacked: true
}
},
},
};
const chart = new Chart('chart', config);
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/3.8.0/chart.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/chartjs-plugin-annotation/1.4.0/chartjs-plugin-annotation.min.js"></script>
<canvas id="chart" height="110"></canvas>

how to highlight stack bar in ChartJS with onclick

Is it possible to select a column in a stack bar chart with a colored border? Like this
I started from here:
https://jsfiddle.net/sdfx/hwx9awgn/
// Return with commas in between
var numberWithCommas = function(x) {
return x.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",");
};
var dataPack1 = [40, 47, 44, 38, 27, 31, 25];
var dataPack2 = [10, 12, 7, 5, 4, 6, 8];
var dataPack3 = [17, 11, 22, 18, 12, 7, 5];
var dates = ["SN 1.0.1", "SN 1.0.2", "SN 1.0.3", "SN 1.0.4", "SN 1.0.5", "SN 2.0.0", "SN 2.0.1"];
// Chart.defaults.global.elements.rectangle.backgroundColor = '#FF0000';
var bar_ctx = document.getElementById('bar-chart');
var bar_chart = new Chart(bar_ctx, {
type: 'bar',
data: {
labels: dates,
datasets: [{
label: 'Bad Style',
data: dataPack1,
backgroundColor: "#512DA8",
hoverBackgroundColor: "#7E57C2",
hoverBorderWidth: 0
}, {
label: 'Warning',
data: dataPack2,
backgroundColor: "#FFA000",
hoverBackgroundColor: "#FFCA28",
hoverBorderWidth: 0
}, {
label: 'Error',
data: dataPack3,
backgroundColor: "#D32F2F",
hoverBackgroundColor: "#EF5350",
hoverBorderWidth: 0
}, ]
},
options: {
animation: {
duration: 10,
},
tooltips: {
mode: 'label',
callbacks: {
label: function(tooltipItem, data) {
return data.datasets[tooltipItem.datasetIndex].label + ": " + numberWithCommas(tooltipItem.yLabel);
}
}
},
scales: {
xAxes: [{
stacked: true,
gridLines: {
display: false
},
}],
yAxes: [{
stacked: true,
ticks: {
callback: function(value) {
return numberWithCommas(value);
},
},
}],
}, // scales legend: {display: true} } // options } );
Yes what you can do is in the onClick as second argument you get an array with all the active elements. So what you can do is loop over each dataset in your chart and see if the index matches one of the list with active elements.
If this is the case you give it a borderWidth of 1, else you put it to 0. After you did this you call chart variable.update();
To make this work you need to remove the hoverBorderWidth of all datasets and give all datasets a borderColor of yellow
Use attribute borderColor with an array and borderWith with and object.
datasets: [
{
label: 'Bad Style',
data: dataPack1,
backgroundColor: "#512DA8",
borderColor: [
'rgb(243, 255, 51)',
'rgb(81,45,168)',
'rgb(243, 255, 51)',
'rgb(81,45,168)',
'rgb(81,45,168)',
'rgb(81,45,168)',
'rgb(81,45,168)',
],
borderWidth: {
top: 0,
right: 4,
bottom: 0,
left: 4
},
},
rgb(243, 255, 51) is yellow color.
To change the color, try this:
var borderColor = [
'rgb(81,45,168)',
'rgb(81,45,168)',
'rgb(81,45,168)',
'rgb(81,45,168)',
'rgb(81,45,168)',
'rgb(81,45,168)',
'rgb(81,45,168)',
];
var bar_ctx = document.getElementById('bar-chart');
var bar_chart = new Chart(bar_ctx, {
type: 'bar',
data: {
labels: dates,
datasets: [
{
label: 'Bad Style',
data: dataPack1,
backgroundColor: "#512DA8",
hoverBackgroundColor: "#7E57C2",
hoverBorderWidth: 0,
borderColor: borderColor,
borderWidth: '4',
},
{
label: 'Warning',
data: dataPack2,
backgroundColor: "#FFA000",
hoverBackgroundColor: "#FFCA28",
hoverBorderWidth: 0,
borderColor: borderColor,
borderWidth: '4',
},
{
label: 'Error',
data: dataPack3,
backgroundColor: "#D32F2F",
hoverBackgroundColor: "#EF5350",
hoverBorderWidth: 0,
borderColor: borderColor,
borderWidth: '4',
},
]
},
options: {
onClick: function(e){
var element = this.getElementAtEvent(e);
borderColor[element[0]._index] = 'rgb(244,140,4)';
this.update();
},
animation: {
duration: 10,
},
tooltips: {
mode: 'label',
callbacks: {
label: function(tooltipItem, data) {
return data.datasets[tooltipItem.datasetIndex].label + ": " + numberWithCommas(tooltipItem.yLabel);
}
}
},
scales: {
xAxes: [{
stacked: true,
gridLines: { display: false },
}],
yAxes: [{
stacked: true,
ticks: {
callback: function(value) { return numberWithCommas(value); },
},
}],
}, // scales
legend: {display: true}
} // options
}
);

Chart.js annotation horizontal line on double y-axis graph

I have been looking for the mistakes I may have made in my codes but still, the horizontal line does not appear on my graph somehow. Is it because of my double y-axis graph?
Below is my code for the graph:
var canvas = document.getElementById('chart').getContext("2d");
new Chart(canvas, {
type: 'line',
data: {
labels: ['Morning 6am-12pm', 'Afternoon 12pm-4pm', 'Evening 4pm-7pm', 'Night 7pm-12am', 'Dawn 12am-6am'],
datasets: [{
label: 'Temperature',
yAxisID: 'A',
data: [30, 32, 33, 31, 30]
}, {
label: 'Humidity',
yAxisID: 'B',
data: [80, 77, 74, 79, 83],
lineTension: 0.3,
fill: false,
borderColor: 'lightblue',
backgroundColor: 'transparent',
pointBorderColor: 'lightblue',
pointBackgroundColor: 'lightgreen',
pointRadius: 5,
pointHoverRadius: 15,
pointHitRadius: 30,
pointBorderWidth: 2
}]
},
options: {
scales: {
yAxes: [{
id: 'A',
type: 'linear',
position: 'left',
}, {
id: 'B',
type: 'linear',
position: 'right',
ticks: {
max: 100,
min: 0
}
}]
},
annotation: {
annotations: [{
type: 'line',
mode: 'horizontal',
scaleID: 'y-axis-0',
value: 32,
borderColor: 'rgb(75, 0, 0)',
borderWidth: 4,
label: {
enabled: false,
content: 'Test label'
}
}]
}
}
});
And this is the result of my graph:
You've changed the ID of the y-axes to A and B:
yAxes: [{
id: 'A',
...
}, {
id: 'B',
...
In the annotation plugin configuration you tell it to draw on y-axis-0 (which is the default ID):
annotation: {
annotations: [{
scaleID: 'y-axis-0',
...
Change the configuration to whichever y-axis you want to draw the annotation to:
scaleID: 'A'
Edit: Working example with Chart.js 2.7.2
var canvas = document.getElementById('chart').getContext("2d");
new Chart(canvas, {
type: 'line',
data: {
labels: ['Morning 6am-12pm', 'Afternoon 12pm-4pm', 'Evening 4pm-7pm', 'Night 7pm-12am', 'Dawn 12am-6am'],
datasets: [{
label: 'Temperature',
yAxisID: 'A',
data: [30, 32, 33, 31, 30]
}, {
label: 'Humidity',
yAxisID: 'B',
data: [80, 77, 74, 79, 83],
lineTension: 0.3,
fill: false,
borderColor: 'lightblue',
backgroundColor: 'transparent',
pointBorderColor: 'lightblue',
pointBackgroundColor: 'lightgreen',
pointRadius: 5,
pointHoverRadius: 15,
pointHitRadius: 30,
pointBorderWidth: 2
}]
},
options: {
scales: {
yAxes: [{
id: 'A',
type: 'linear',
position: 'left',
}, {
id: 'B',
type: 'linear',
position: 'right',
ticks: {
max: 100,
min: 0
}
}]
},
annotation: {
annotations: [{
type: 'line',
mode: 'horizontal',
scaleID: 'A',
value: 32,
borderColor: 'rgb(75, 0, 0)',
borderWidth: 4,
label: {
enabled: false,
content: 'Test label'
}
}]
}
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.7.2/Chart.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/chartjs-plugin-annotation/0.5.7/chartjs-plugin-annotation.js"></script>
<canvas id="chart"></canvas>

Is it possible to dynamically add more Y Axes or more X Axes on chart js?

I've been trying for a few days to dynamically add custom scales to my chart, using chart js. I am using Angular 2 and Typescript.
I tried it in two different ways. The first one triggers an error. The second one works but it's not what i am aiming for. So what am I doing wrong?
First method:
I have a modal which has some inputs to set a custom scale and it returns an object that looks like the scale object from the Chart JS documentation:
customScale = {
id: 'y-axis-2',
type: 'linear',
position:'right',
scaleLabel: {
display: true,
labelString: 'scale 3'
},
ticks: {
max: 20,
min: 1
}
}
I have a function who is supposed to add that scale to the scales array attached to my chart.
addCustomScale(customScale: any, axis: string) {
const ci = this.chartComponent.chart;
let scale = [];
scale.push(customScale);
if (axis === 'Axis Y') {
ci.options.scales.yAxes = Chart.helpers.scaleMerge(Chart.defaults.scale,{yAxes: scale}).yAxes;
} else {
ci.options.scales.xAxes = Chart.helpers.scaleMerge(Chart.defaults.scale, {xAxes: scale}).xAxes;
}
ci.update();
}
When calling this function, i get an error:
ERROR TypeError: Cannot set property 'options' of undefined
at core.controller.js:51
So far my chart looks like this:
<canvas width="600" height="300" id="canvas" #myCanvas baseChart
[datasets]="lineChartData"
[labels]="lineChartLabels"
[options]="mfChartOptions"
[legend]="lineChartLegend"
[chartType]="lineChartType"></canvas>
I defined everything in typescript:
import {BaseChartDirective} from 'ng2-charts';
declare const Chart: any;
#Component({
selector: 'app-chart',
templateUrl: './chart.component.html',
styleUrls: ['./chart.component.css']
})
export class ChartComponent {
#ViewChild(BaseChartDirective) chartComponent: BaseChartDirective;
public lineChartData: Array<any> = [
{data: [65, 59, 80, 81, 56, 55, 40], yAxisID: 'y-axis-0', label: 'Series A'},
{data: [28, 48, 40, 19, 86, 27, 90], yAxisID: 'y-axis-1', label: 'Series B'},
{data: [18, 48, 77, 9, 100, 27, 40], yAxisID: 'y-axis-1', label: 'Series C'}
];
public lineChartLabels: Array<any> = ['January', 'February', 'March', 'April', 'May', 'June', 'July'];
public lineChartLegend = true;
public lineChartType = 'line';
public mfChartOptions = {
responsive: true,
pan: {
enabled: true,
mode: 'xy'
},
zoom: {
enabled: true,
drag: true,
mode: 'xy'
},
legend: {
position: 'bottom',
},
scales: {
yAxes: [{
id: 'y-axis-0',
type: 'linear',
position: 'left',
}, {
id: 'y-axis-1',
type: 'linear',
position: 'right',
ticks: {
max: 100,
min: 0
}
}]
},
annotation: {
events: ['click'],
annotations: []
},
tooltipTemplate: '<%if (label){%><%=label %>: <%}%><%= value + \' %\' %>',
tooltips: {
intersect: true,
bodySpacing: 4
},
plugins: {
filler: {
propagate: true
}
}
};
Second method. I just added another scale to the scales that i already have in options, like this:
public mfChartOptions = {
responsive: true,
pan: {
enabled: true,
mode: 'xy'
},
zoom: {
enabled: true,
drag: true,
mode: 'xy'
},
scales: {
yAxes: [{
id: 'y-axis-0',
type: 'linear',
position: 'left',
}, {
id: 'y-axis-1',
type: 'linear',
position: 'right',
ticks: {
max: 100,
min: 0
}
}, {
id: 'y-axis-2',
type: 'linear',
position: 'right',
ticks: {
max: 80,
min: 40
}
}]
},
annotation: {
events: ['click'],
annotations: []
},
tooltipTemplate: '<%if (label){%><%=label %>: <%}%><%= value + \' %\' %>',
tooltips: {
intersect: true,
bodySpacing: 4
},
plugins: {
filler: {
propagate: true
}
}
};
And changed my dataset to:
public lineChartData: Array<any> = [
{data: [65, 59, 80, 81, 56, 55, 40], yAxisID: 'y-axis-0', label: 'Series A'},
{data: [28, 48, 40, 19, 86, 27, 90], yAxisID: 'y-axis-1', label: 'Series B'},
{data: [18, 48, 77, 9, 100, 27, 40], yAxisID: 'y-axis-2', label: 'Series C'}
];
This one works just fine, but i want to allow the user to create a custom scale, instead of hard coding it.
you can destroy the chart and run new Chart function again.
Here is fiddle : https://jsfiddle.net/cbalakus/fhadc9by/
function addYAxis() {
var leng = window.myLine.options.scales.yAxes.length + 1;
var clr = chartColors[Math.floor(Math.random() * chartColors.length)];
opts.scales.yAxes.push({
type: "linear",
display: true,
position: "right",
id: "y-axis-" + leng,
ticks: {
fontColor: clr
}
});
lineChartData.datasets.push({
label: "y-axis-" + leng,
borderColor: clr,
backgroundColor: clr,
fill: false,
data: getDatas(),
yAxisID: "y-axis-" + leng,
});
window.myLine.destroy();
var ctx = document.getElementById("cvs").getContext("2d");
window.myLine = Chart.Line(ctx, {
data: lineChartData,
options: opts
});
}

Categories