Load more historical data in Line charts on panning left - chart.js

I am plotting line chart data using ChartsModule from ng2-charts. I have also added a plugin to enable zoom and pan.
The problem is I have a lot of data to show and plot, but on page load, I am loading only last 1-day data and want to give the user an option to see historical data by panning to left.
I am getting onPanComplete event on chart panning and able to update the datasets but not sure how I will know that the user has reached to the left end of the chart so that I will load more data and update the datasets.
Below is my chart component:
import { Component, OnInit } from '#angular/core';
#Component({
selector: 'app-chart',
templateUrl: './chart.component.html',
styleUrls: ['./chart.component.scss']
})
export class ChartComponent implements OnInit {
lineChartData: any;
chartDataSets: any;
constructor() { }
public lineChartOptions
ngOnInit() {
var sampleData = [
{
"CreateDate": "2019-08-12 15:30:03",
"NewBuyValue": "16,791.658178"
},
{
"CreateDate": "2019-08-12 14:30:29",
"NewBuyValue": "16,760.46713691"
},
{
"CreateDate": "2019-08-12 13:30:31",
"NewBuyValue": "16,792.80212024"
},
{
"CreateDate": "2019-08-12 12:30:27",
"NewBuyValue": "16,740.86970638"
}
].map(d => {
return {
t: new Date(d.CreateDate),
y: d.NewBuyValue.replace(',', '')
}
})
this.chartDataSets = [{
label: 'Buy Price',
backgroundColor: "#2072ef",
borderColor: '#2072ef',
type: 'line',
pointRadius: 0,
fill: false,
lineTension: 0,
borderWidth: 2,
data: sampleData
}]
this.lineChartOptions = {
pan: {
enabled: true,
mode: 'x',
onPan: function (e) {
console.log(`I'm panning!!!`, e);
},
onPanComplete: function (e) {
console.log(`I was panned!!!`, e);
e.chart.config.data.datasets[0].data.pop()
}
},
zoom: {
enabled: true,
mode: 'x',
},
hover: {
mode: 'nearest',
intersect: true
},
responsive: true,
scales: {
xAxes: [{
scaleLabel: {
display: true,
labelString: 'Time'
},
type: 'time',
distribution: 'series',
ticks: {
source: 'data',
autoSkip: false
}
}],
yAxes: [{
scaleLabel: {
display: true,
labelString: 'Prices'
}
}]
},
tooltips: {
intersect: false,
mode: 'index',
callbacks: {
label: function (tooltipItem, myData) {
var label = myData.datasets[tooltipItem.datasetIndex].label || '';
if (label) {
label += ': ';
}
label += parseFloat(tooltipItem.value).toFixed(2);
return label;
}
}
}
}
this.lineChartData = []
}
}
Also, I would like to know If there is any better way to implement history data load on panning event

I loaded 1 week data and updated time.min for xAxis to the value of the current date start. That's done the trick
this.lineChartOptions.scales.xAxes[0].time.min = chartData.data[0].t - 24 * 3600000

Related

Multiple bar charts side by side chartjs

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>

Chartjs show hidden data on tooltip

Good Day,
I have a bar chart with multiple datasets for the chart. I would like to hide all the bars except for one (a Totals if you will), and on the Tooltip, I want to show all of the data in all the datasets. Unfortunately, the tooltip only shows the visible datasets. Does anyone know how to show all the data sets?
If you run this with
<canvas id="myChart" width="400" height="400"></canvas>
Hover over the chart and the first dataset (labeled 'First Label') is not shown. How do I show that in the tooltip? Does anyone know?
var ds1 = [], ds2 = [], ds3 = [], ds4 = [], ds5 = [], ds6 = [], labels = [];
for(var i = 0; i < 2; i++){
labels.push('Label: ' + i);
ds1.push(i);
ds2.push(i+1);
ds3.push(i+2);
ds4.push(i+3);
ds5.push(i+4);
ds6.push(i+5);
}
const dataSets = {
labels: labels,
datasets: [
{
label: 'First Label',
hidden: true,
data: ds1
},{
label: 'Second Label',
data: ds2
},{
label: 'Third Label',
data: ds3
},{
label: 'Fourth Label',
data: ds4
},{
label: 'Fifth Label',
data: ds5
},{
label: 'Totals',
data: ds6
}
]
}
var myChart = new Chart(ctx, {
type: 'horizontalBar',
data: dataSets,
elements: {
rectangle: {
borderWidth: 2
}
},
responsive: true,
legend: {
display: false
},
title: {
display: false
},
scales: {
yAxes: [
{
barThickness: 15
}
],
xAxes: [
{
ticks: {
suggestedMin: 0,
suggestedMax: 50
},
minBarLength: 5
}]
}
});
Thanks,
Tim
If you hide all bars except one, you can define a tooltips.callback function for label. This function collects the labels and appropriate values from all datasets using Array.map() and returns a string array.
tooltips: {
callbacks: {
label: (tooltipItem, chart) => dataSets.datasets.map(ds => ds.label + ': ' + ds.data[tooltipItem.index])
}
},
Please have a look at your amended code below:
var ds1 = [], ds2 = [], ds3 = [], ds4 = [], ds5 = [], ds6 = [], labels = [];
for (var i = 0; i < 2; i++) {
labels.push('Label: ' + i);
ds1.push(i);
ds2.push(i + 1);
ds3.push(i + 2);
ds4.push(i + 3);
ds5.push(i + 4);
ds6.push(5 * i + 10);
};
const dataSets = {
labels: labels,
datasets: [{
label: 'First Label',
hidden: true,
data: ds1
}, {
label: 'Second Label',
hidden: true,
data: ds2
}, {
label: 'Third Label',
hidden: true,
data: ds3
}, {
label: 'Fourth Label',
hidden: true,
data: ds4
}, {
label: 'Fifth Label',
hidden: true,
data: ds5
}, {
label: 'Totals',
data: ds6
}]
};
var myChart = new Chart('myChart', {
type: 'horizontalBar',
responsive: true,
data: dataSets,
elements: {
rectangle: {
borderWidth: 2
}
},
options: {
legend: {
display: false
},
title: {
display: false
},
tooltips: {
callbacks: {
label: (tooltipItem, chart) => dataSets.datasets.map(ds => ds.label + ': ' + ds.data[tooltipItem.index])
}
},
scales: {
xAxes: [{
ticks: {
min: 0,
stepSize: 1
}
}]
}
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.9.3/Chart.min.js"></script>
<canvas id="myChart" height="100"></canvas>
If you wanted to display the legend and allow users to show additional bars through a mouse click on individual legend labels, the callback function would be invoked once for each visible bar. Therefore you would have to make sure to return the labels array only once and return null in all other cases. The following code would return the labels array only for the 'Totals' bar.
tooltips: {
callbacks: {
label: (tooltipItem, chart) => {
if (tooltipItem.datasetIndex == dataSets.datasets.length - 1) {
return dataSets.datasets.map(ds => ds.label + ': ' + ds.data[tooltipItem.index]);
}
return null;
}
}
},
This would not work however, if the user decides to hide the 'Totals' bar. The code could however be improved to also overcome this limitation.

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

increase the gap between y-axis and first x-axis value chart.js

I want to display points in the line chart of chart.js, however, points one first and the last index at x-axis don't show up completely as shown in the image
How can I increase the gap between the first and last values of x-axis from the y-axis. Here is my code for the chart
var config = {
type: 'line',
data: {
labels: ['1809_1970', '1971_1975', '1976_1980', '1981_1985', '1986_1990', '1991_1995', '1996_2000','2001_2005','2006_2010','2011_2012','2013_2015'],
datasets: [d1,d2,d3]
},
options: {
responsive: true,
title: {
display: true,
text: 'points chart',
},
tooltips: {
callbacks: {
label: function(tooltipItem, data) {
var label = data.datasets[tooltipItem.datasetIndex].cui[tooltipItem.xLabel] || '';
return 'CUI'+' :: '+label ;
},
afterLabel: function(tooltipItem, data) {
var cui_name = data.datasets[tooltipItem.datasetIndex].cui_name[tooltipItem.xLabel] || '';
return ['NAME'+' :: '+cui_name];
}
}
},
hover: {
mode: 'index',
intersect: true
},
pan: {
enabled: true,
mode: 'y'
},
zoom: {
enabled: true,
mode: 'y',
speed: 0.025
},
scales: {
xAxes: [{
display: true,
scaleLabel: {
display: true,
labelString: 'Time Frame'
}
}],
yAxes: [{
display: true,
scaleLabel: {
display: true,
labelString: 'Score'
}
}]
}
}
};
I needed to accomplish the same thing, and I was able to get the desired result bby adding offset: true to the xAxes options.
xAxes: [{
offset: true, // <-- This right here
display: true,
scaleLabel: {
display: true,
labelString: 'Time Frame'
}
}],

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
}]
}
}
});
}