similar questions has been asked here in the past but eventually for older versions of Chart.js. The accepted answers of the questions didn't work.
I have a chart like this:
The code of this chart is this:
new Chart(
document.getElementById("chart-01"), {
type: 'bar',
data: {
labels: ["Test loooooong 1", "Test loooooong 2", "Test loooooong 3"],
datasets: [{ data: [574.48, 140.93, 64.37]}]
},
options: {
scales: {
y: {
ticks: {
callback: function(value) {
return '$ ' + value;
}
}
},
// x : {
// ticks : {
// callback : function(value, index, ticks) { return value.split(0,8)+'...';}
// }
// }
},
plugins: {
legend: {
display: false
}
}
}
}
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/4.2.1/chart.umd.js" integrity="sha512-vCUbejtS+HcWYtDHRF2T5B0BKwVG/CLeuew5uT2AiX4SJ2Wff52+kfgONvtdATqkqQMC9Ye5K+Td0OTaz+P7cw==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
<canvas id="chart-01" width="500" height="600"></canvas>
I am trying to truncate the names on the x-axis after 8 chars and add a '...', So if I am using that commented code block:
x : {
ticks : {
callback : function(value, index, ticks) { return value.split(0,8)+'...';}
}
}
I get the error message:
Uncaught (in promise) TypeError: value.split is not a function
Also if I use only the value, without split, I get numbers back but not the label names.
This are links to solutions that did not work for me.
https://stackoverflow.com/a/39537545/3408158
https://stackoverflow.com/a/44418430/3408158
What am I doing wrong?
To get/change the value of the label you have to use the function getLabelForValue.
(here an example from the documentation)
And split is the incorrect function you would need to use substring.
(link to the mdn documentation)
Here all the fixes entered into your example code:
new Chart(document.getElementById("chart"), {
type: 'bar',
data: {
labels: ["Test looooong 1", "Test looooong 2", "Test looooong 3"],
datasets: [{ data: [574.48, 140.93, 64.37]}]
},
options: {
scales: {
y: {
ticks: { callback: value => `$ ${value }` }
},
x: {
ticks: {
callback: function(value){
let newLabel = this.getLabelForValue(value)
.substring(0,8) + '...';
return newLabel;
}
}
}
}
}
});
<script src="//cdn.jsdelivr.net/npm/chart.js"></script>
<div class="chart" style="height:184px; width:350px;">
<canvas id="chart" ></canvas>
</div>
Information: The mentioned solutions don't worked because, as far as I can tell, they are base on he chartjs Verion 2.0+, and since then the chartjs library change substantially.
https://plnkr.co/edit/O4BxVsdOZBc4R68p
fetch(target)
.then(response => response.json())
.then(data => {
var prices = data['Time Series (5min)'];
for (prop in prices) {
var stockPrices = prices[prop]['1. open'];
//change to 2. high, etc
console.log(`${prop} : ${stockPrices}`);
stocksData.datasets[0].data.push({x: prop, y: +stockPrices})
//time x axes are preventing render
window.lineChart.update();
}
})
I am getting information from the AlphaVantage API and am trying to graph the time as the X axis and the open price as the Y axis. However, the time from the API is in an odd format and doesn't appear to graph. I have looked into Moment.js but that appears to be making times, not formatting them. Can anyone give me any pointers on graphing the time correct?
Your problem comes from 2 things:
Your Chart config in options with xAxes that should be xAxis instead
Missing Labels and correct data in Chart data
Here is the codes that works:
var stocksData = {
datasets: [
{
label: 'open',
backgroundColor: 'rgba(104,0,255,0.1)',
data: [
],
},
],
};
window.onload = function() {
var ctx = document.getElementById('myChart').getContext('2d');
var lineChart = new Chart(ctx, {
type: 'line',
data: stocksData,
options: {
scales: {
xAxis: [
{
type: 'linear',
position: 'bottom',
},
],
},
},
});
window.lineChart = lineChart;
};
var sym = 'AAPL'; //get from form
var tseries = 'TIME_SERIES_INTRADAY'; //get from form
var target = `https://www.alphavantage.co/query?function=${tseries}&symbol=${sym}&interval=5min&apikey=VA3RZ8B9PPYWKQKN`;
function update () {
fetch(target)
.then(response => response.json())
.then(data => {
var prices = data['Time Series (5min)'];
for (prop in prices) {
var stockPrices = prices[prop]['1. open'];
//change to 2. high, etc
console.log(`${prop} : ${stockPrices}`);
//stocksData.datasets[0].data.push({x: prop, y: +stockPrices})
stocksData.datasets[0].data.push(stockPrices);
// Format date here. For example with Moment:
// var date = moment(prop).format('YYYY-MM-DD')
stocksData.labels.push(prop);
//time x axes are preventing render
window.lineChart.update();
}
})
.catch(error => console.error(error));
}
A complete format for Chart data would be like:
var stocksData = {
labels: ['date1', 'date2', 'date3', 'date4'],
datasets: [
{
label: 'open',
backgroundColor: 'rgba(104,0,255,0.1)',
data: [
'data1', 'data2', 'data3', 'data4'
],
},
],
};
Then each data and date label should be push separately:
stocksData.datasets[0].data.push(stockPrices);
stocksData.labels.push(prop);
To format with Moment you can use:
var dateStr = moment(prop).format('YYYY-MM-DD');
The "odd" time format is (almost) the standard international datetime format. In this case YYYY-MM-DD HH:MM:SS. I strongly suggest you familiarise yourself with it and use it in preference to DD/MM/YYYY or MM/DD/YYYY.
You can fix your code by changing the x-axis type to time and adding the appropriate configuration options:
options: {
scales: {
xAxes: [
{
type: 'time',
...
Note that you'll also need to change your call to Chart.js to the version with moment.js bundled (or include moment separately):
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.9.3/Chart.bundle.min.js"></script>
I´m developing a visualization module for some crypto portfolios with vue.js and chart.js but am currently stuck with this:
Empty chart is displayed but non of the values are rendered.
Since the values are dynamically loaded after the chart is initialized I believe that the chart is not updating itself properly (even though I call .update()), but no errors are displayed whatsoever.
I wrapped the chart.js rendering in a vue component:
Vue.component('portfolioValues', {
template: '<canvas width="400" height="200"></canvas>',
data: function() {
return {
portfolio_value: [],
portfolio_labels: [],
chart: null,
}
},
methods: {
load_portfolio_value_local: function() {
values = [];
labels = []
local_data.forEach(element => {
values.push(element.total_usd);
labels.push(moment(element.timestamp, 'X'));
});
this.portfolio_value = values;
this.portfolio_labels = labels;
this.chart.update();
},
render_chart: function() {
this.chart = new Chart(this.$el, {
type: 'line',
data: {
labels: this.portfolio_labels,
datasets: [{
label: "Portfolio Value",
data: this.portfolio_value,
}]
},
options: {
scales: {
xAxes: [{
type: 'time',
distribution: 'linear',
}]
}
}
});
}
},
mounted: function() {
this.render_chart();
this.load_portfolio_value_local();
}
});
For demonstration purposes I just added some data locally, looks like this:
local_data = [{
"timestamp": 1515102737,
"total_btc": 0.102627448096786,
"total_usd": 1539.41274772627
}, {
"timestamp": 1515102871,
"total_btc": 0.102636926127186,
"total_usd": 1538.52649627725
}, {
"timestamp": 1515103588,
"total_btc": 0.102627448096786,
"total_usd": 1532.33042753311
}
]
Here is the full demo code: https://codepen.io/perelin/pen/mppbxV
Any ideas why no data gets rendered? thx!
The problem you have here is how vuejs handles its data.
If you use it like that:
local_data.forEach(element => {
this.portfolio_value.push(element.total_usd);
this.portfolio_labels.push(moment(element.timestamp, 'X'));
});
this.chart.update();
The chart will update. But by re-initializing the arrays you work against vuejs.
TL;DR
If you want to re-initialize an object, you could assign the array to the object:
Object.assign(this.portfolio_value, values);
Object.assign(this.portfolio_labels, labels);
That way, the linking stays working.
I am attempting to create a dynamic flot graph dependant upon the data given to it, my flot graph is using JSON for its information and here is an example of the dataset:
{
"total":[[1377691200,115130],[1377694800,137759],[1377698400,137759],[1377702000,137759],[1377705600,137759],[1377709200,139604],[1377712800,137759],[1377716400,137759],[1377720000,137759],[1377723600,137759],[1377727200,137759],[1377730800,138156],[1377734400,137759],[1377738000,137759],[1377741600,137759],[1377745200,137759],[1377748800,138156],[1377752400,137759],[1377756000,137759],[1377759600,168831],[1377763200,137759],[1377766800,0],[1377770400,0]],
"dev0":[[1377691200,115130],[1377694800,137759],[1377698400,137759],[1377702000,137759],[1377705600,137759],[1377709200,139604],[1377712800,137759],[1377716400,137759],[1377720000,137759],[1377723600,137759],[1377727200,137759],[1377730800,138156],[1377734400,137759],[1377738000,137759],[1377741600,137759],[1377745200,137759],[1377748800,138156],[1377752400,137759],[1377756000,137759],[1377759600,168831],[1377763200,137759],[1377766800,0],[1377770400,0]],
"dev1":[[1377691200,0],[1377694800,0],[1377698400,0],[1377702000,0],[1377705600,0],[1377709200,0],[1377712800,0],[1377716400,0],[1377720000,0],[1377723600,0],[1377727200,0],[1377730800,0],[1377734400,0],[1377738000,0],[1377741600,0],[1377745200,0],[1377748800,0], [1377752400,0],[1377756000,0],[1377759600,0],[1377763200,0],[1377766800,0],[1377770400,0]]
}
The script i have created already:
$(".bytes_portal_pop").click(function(e) {
e.preventDefault();
var graph=$(this).data('graph');
var range=$(this).data('range');
var divid=$(this).data('divid');
var title=$(this).data('boxtitle');
$.getJSON("/action/sites/GetFlotStats/?graph=" + graph + "&range=" + range, function(json) {
//succes - data loaded, now use plot:
var plotarea = $("#" + divid);
var dev0=json.dev0;
var dev1=json.dev1;
$.plot(
$("#" + divid),
[
{
data: dev0,
lines:{show: true, fill: true},
label: "dev0",
},
{
data: dev1,
lines:{show: true, fill: true},
label: "dev1",
},
],
{
xaxis: {mode:"time"},
grid: {hoverable: true},
tooltip: true,
tooltipOpts: {
content: "Traffic: %y GB"
}
}
);
});
$("#boxtitleB_flot").html(title);
});
This way works fine and displays the two lines as i need however i would like it to be dynamic i.e. so i dont have to define each graph line i believe todo this i simply need a for or each() loop on the
var dev0=json.dev0;
and
{
data: dev0,
lines:{show: true, fill: true},
label: "dev0",
},
Any help achieving this would be much appreciated.
Correct, just loop it and generate your series objects dynamically.
Given a json return like:
jsonObj = {
"total":[[1377691200,115130],[1377694800,137759],[1377698400,137759],[1377702000,137759],[1377705600,137759],[1377709200,139604],[1377712800,137759],[1377716400,137759],[1377720000,137759],[1377723600,137759],[1377727200,137759],[1377730800,138156],[1377734400,137759],[1377738000,137759],[1377741600,137759],[1377745200,137759],[1377748800,138156],[1377752400,137759],[1377756000,137759],[1377759600,168831],[1377763200,137759],[1377766800,0],[1377770400,0]],
"dev0":[[1377691200,115130],[1377694800,137759],[1377698400,137759],[1377702000,137759],[1377705600,137759],[1377709200,139604],[1377712800,137759],[1377716400,137759],[1377720000,137759],[1377723600,137759],[1377727200,137759],[1377730800,138156],[1377734400,137759],[1377738000,137759],[1377741600,137759],[1377745200,137759],[1377748800,138156],[1377752400,137759],[1377756000,137759],[1377759600,168831],[1377763200,137759],[1377766800,0],[1377770400,0]],
"dev1":[[1377691200,0],[1377694800,0],[1377698400,0],[1377702000,0],[1377705600,0],[1377709200,0],[1377712800,0],[1377716400,0],[1377720000,0],[1377723600,0],[1377727200,0],[1377730800,0],[1377734400,0],[1377738000,0],[1377741600,0],[1377745200,0],[1377748800,0], [1377752400,0],[1377756000,0],[1377759600,0],[1377763200,0],[1377766800,0],[1377770400,0]]
};
Create an array of series like:
var series = [];
$.each(jsonObj, function (key, val) {
var serie = {};
serie.label = key;
serie.data = val;
series.push(serie);
});
And then create the plot:
$.plot(
$("#placeholder"),
series,
{}
);
Fiddle here.