angular chartjs not showing data properly - javascript

I'm using Angular 6 and trying to implement a Chart.js line chart. Basically, I'm calling my API for two arrays: a weight_data array and a weight_date array and using them for the chart.
I store the arrays I got from the API request in two instances: this.weight_date and this.weight_data.
Here's the code for the chart:
// code for chart here
this.chart = new Chart(this.chartRef.nativeElement, {
type: 'line',
data: {
labels: this.weight_date, // your labels array
datasets: [
{
data: this.weight_data, // your data array
borderColor: '#e0573a',
fill: true
}
]
},
options: {
legend: {
display: false
},
scales: {
xAxes: [{
display: true
}],
yAxes: [{
display: true
}],
},
title: {
display: true,
text: 'Weight Tracker',
fontFamily: "'Montserrat', sans-serif",
fontColor: '#272727',
fontSize: 18,
padding: 12
},
layout: {
padding: {
left: 10,
right: 20,
top: 0,
bottom: 0
}
},
gridLines: {
drawOnChartArea: true
},
}
});
It's works okay when I use pre-coded arrays (["11/02/18", "11/03/18", "11/04/18"] for dates and [65, 66, 67] for weight). But when I try to use the two instances, the chart comes up blank. There's no error of sorts, it's just blank.
I'm doing the API call first then initializing the chart, respectively, in ngOnInit. What am I missing here?

It looks like you want to subscribe to an observable which can provide the data, and once it comes in, you can then initialize your chart. Usually, we would want to have the HTTP call occur within a service, but to simplify, this is how we could do with within a component:
import {HttpClient} from '#angular/common/http';
import {Observable} from 'rxjs/Observable';
import {Component, OnInit} from '#angular/core';
export class MyChart implements OnInit {
constructor(private http: HttpClient) { }
private getData() {
let url = '[SOME-URL]' // your path to the data API endpoint
return this.http.get(url) as Observable<Array<any>>; // you would want to use correct typings here, instead of <any>
}
ngOnInit() {
this.getData().subscribe((data: any) => {
console.log(data);
// assign your data here, and then initialize your chart.
});
}
}

I just found out what I was doing wrong: I'm using axios, which is promise-based, so taking #JeffFhol's suggestion into account, my two instances were blank since I was initializing the chart at the same time I was making the API request.
The solution was to initialize the chart in the then clause of the axios request, which ensures that my instances aren't empty.
Here's the snippet that worked:
axios({
method: 'post',
url: 'url_to_api',
headers: {
'Content-Type' : 'application/json'
// custom data headers, etc.
},
data: {
// data to be sent
}
})
.then(response => {
this.weight_dates = response.data.weight_date;
this.weight_data = response.data.weight_data;
// code for chart here
})
.catch(error => {
console.log(error)
})

Related

maximum calstack size exceeded on vuex commit

I have this error here
I could limit the error to this line here
mounted() {
this.$nextTick(() => {
let ctx = this.$refs.canvas.getContext('2d')
let { chartType, dataOptions } = this.module
this.chart = new Chart(ctx, {
type: chartType,
data: dataOptions,
options: minimizeOptions,
})
})
},
The error comes from dataOptions. If i set data to {} everything works ok, but obviously my chart has no data then.
this.module is an prop that is being passed to my component. The component itself gets rendered in an v-for loop
<module
v-for="mod in modules"
:module="mod"
:key="mod._id.toString()"
/>
I am using here Chart.js.
I cannot find the reason for this call stack exceed error.
Maybe somebody had similar problems?
I also need to mention that this error happens when i want to toggle an global component that is placed in an layout layout
dataOptions:
{
datasets: [
{
backgroundColor: "#34495e",
borderColor: "bdc3c7",
data: [0],
label: "My first dataset"
}
],
labels: ["Start"]
}
I actually does not need reactivity on this prop, so i decided to freeze it up.
let { chartType, dataOptions } = Object.freeze(this.module);
Reactivity is gone an the error aswell. This fixed my problem.
Maybe somebody has an solution for reactive data

How to use Chart.js plugin data-labels with ng2-chart?

Well, here I am again with my Angular and javascript woes feeling dumber for each question I ask.
But let me try to explain my initial steps and how it lead to this problem. So, in my latest project, I wanted to throw some fancy charts to make things cleaner and more approachable for the user. Chart.js came to mind... or I should say, ng2-charts.
Things were peachy, stuff looked nice, but... I had two problems. On a horizontal bar, I wanted to dynamically alter the size of the chart, so the user didn't have to scroll for an obscure amount of time to get down the page. So instead of this unwieldy beast...
I try to apply some Angular magic. Later on I want to calculate the size of it on my backend end. For now, a static value shall do.
#ViewChild('inventoryChart') itemChart : ElementRef;
constructor(public renderer: Renderer2) { }
ngAfterViewInit() {
this.renderer.setStyle(this.itemChart.nativeElement, 'height', '230px')
}
Which leads to...
Nice. But my second problem was a lot harder than I assumed it to be. I wanted the charts to have the respective values on each bar chart. I was somewhat shocked to learn, that it wasn't an innate feature of chart.js but a plugin instead. So I tried to narrow down my problems I had by looking at my chart configuration.
#ViewChild('itemChart') itemChart : ElementRef;
//public context: CanvasRenderingContext2D;
public chartType: string = 'horizontalBar';
chartDatalabel: ChartLabel;
public chartDatasets: Array<any> = [
{ data: [28, 20, 6, 5, 3], label: 'Inventory per country' }
];
public chartLabels: Array<any> = ['DEU', 'SVK', 'FRA', 'GBR', 'AUT'];
public chartColors: Array<any> = [
{
backgroundColor: '#0E599A',
pointBackgroundColor: 'rgba(220,220,220,1)',
pointBorderColor: '#fff',
pointHoverBackgroundColor: '#fff',
pointHoverBorderColor: 'rgba(220,220,220,1)'
},
];
public chartOptions: any = {
responsive: true,
maintainAspectRatio: false,
scales: {
yAxes: [{
barPercentage: .9,
categoryPercentage: .9,
gridLines: {
offsetGridLines: true,
display: true,
},
ticks: {
beginAtZero: true
}
}],
xAxes: [{
ticks: {
beginAtZero: true,
min: 0
},
gridLines: {
offsetGridLines: true,
display: true,
},
}]
},
};
Since I had a similar question and by looking at the documentation... it would require the chart to register the plugin.
So I added
import { Chart } from 'chart.js';
But quickly realized, I didn't know how to actually get this chart instance I was creating in my component. And how could I add this in the option for this specific chart only? I didn't find many sources with a similar problem to mine (which made me feel even more inept...).
Is there any clean 'angular-way' to add this plugin in ng2-charts?
EDIT: Judging from the documentation, I could define what the plugin can do in the following ways...
var plugin = { /* plugin implementation */ };
And then calling this plugin in the options of the chart or globally...
Chart.plugins.register({
// plugin implementation
});
Which I would like to avoid. Never a fan of global configuration unless absolutely necessary and time-saving.
EDIT2: Sorta gave up on NOT registering it globally, but it still doesn't help a whole lot. I added to my imports...
import { Chart } from 'chart.js';
import * as ChartLabel from 'chartjs-plugin-datalabels';
And then tried to register the plugin in the global Chart.
ngOnInit() {
Chart.plugins.register(ChartLabel);
}
Which has done 'something' as far as I can tell. So I attempted to do a very basic implementation of the plugin. The other tooltips don't work anymore but it only triggers an error when I hover around the bars.
plugins: {
datalabels: {
color: 'white',
display: true,
font: {
weight: 'bold'
},
formatter: Math.round
}
},
No clue what I can do anymore...
Well, since there was literally no answer, I could ultimately settle for the "Global" Chart solution.
Chart.pluginService.register({
// I gave it an id, so I could disable the plugin if needed
id: 'p1',
afterDatasetsDraw: function (chart, _ease) {
let width = chart.chart.width;
let ctx = chart.chart.ctx;
let datasets = chart.data.datasets;
datasets.forEach(function (_dataset, index) {
let meta = chart.getDatasetMeta(index);
// grabbing the array with the data...
let barLabelData = meta.controller._data;
// console.log(meta.controller._data)
if (!meta.hidden) {
meta.data.forEach(function (segment, _index) {
let model = segment._model;
let position = segment.tooltipPosition();
let x = position.x;
let y = position.y;
let height = model.height;
ctx.restore();
ctx.textBaseline = "middle";
// var fontSize = (height / 114).toFixed(2);
ctx.font = 'bold ' + height / 2 + 'px Arial';
ctx.fillStyle = '#777'; //first label's font color
let text = barLabelData[_index];
ctx.fillText(text, x, y);
ctx.save();
})
}
});
}
});
Which ultimately graced me with a sorta okay looking chart. So yeah, it's not at clean as I would like to. But there are still issues with implementation.
My Chart component globally implements my plugin logic. That's awful deisgn here and I have to decouple it. The minor problem is, I have to make sure the labels work always and are properly depicted. But for now I'm pleased it works.
Here's an example done by #paviad:
https://stackblitz.com/edit/ng2-charts-bar-labels
import { Component, OnInit } from '#angular/core';
import { ChartOptions, ChartType, ChartDataSets } from 'chart.js';
import * as pluginDataLabels from 'chartjs-plugin-datalabels';
import { Label } from 'ng2-charts';
#Component({
selector: 'my-app',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent {
public barChartOptions: ChartOptions = {
responsive: true,
// We use these empty structures as placeholders for dynamic theming.
scales: { xAxes: [{}], yAxes: [{}] },
plugins: {
datalabels: {
anchor: 'end',
align: 'end',
font: {
size: 20,
}
}
}
};
...
public barChartPlugins = [pluginDataLabels];
...
Detailed options are here:
https://chartjs-plugin-datalabels.netlify.app/

Using Highcharts Export Server, how to include commas in labels?

I am creating a Highcharts configuration object in Java (EDIT: yes, Java, on the server side, NOT JavaScript), and sending it to the Highcharts Export Server to get a PNG to use in a PDF.
Highcharts, by default, doesn't include commas in its data labels.
If I were just creating this chart on the client side, I'd add this to the javascript:
Highcharts.setOptions({
lang: {
thousandsSep: ','
}
});
Then, I could make the commas show up.
The problem is, I don't know how to send an equivalent message to the Highcharts Export Server. Currently, I am sending an object like this (EDIT: from the server side, using Java) to the Highcharts Export Server and not getting commas in the labels of the PNG it returns:
{
series: [{
data: [1000, 2000, 1500],
}],
plotOptions: {
series: {
dataLabels: {
enabled: true
}
}
}
}
What can I do or send to the Highcharts Export Server to get the desired effect of putting commas in the labels?
(you can test out objects in the Highcharts Export Server here: http://export.highcharts.com/)
You can you globalOptions parameter in your request to achieve the desired result.
Live demo: http://jsfiddle.net/kkulig/0s7uwsgg/
var optionsStr = JSON.stringify({
"series": [{
dataLabels: {
enabled: 'true'
},
"data": [1000, 1000]
}]
}),
globalOptions = JSON.stringify({
lang: {
thousandsSep: ','
}
}),
dataString = encodeURI('async=true&type=jpeg&width=400&options=' + optionsStr + '&globalOptions=' + globalOptions);
API reference for request parameters: https://github.com/highcharts/node-export-server/blob/master/README.md

setData is not a function + Angular2-Highcharts

I was following simple example to set data to Highcharts using this Angular2-Highcharts module
As shown in the following example, i have made small tweak to dynamically set data to Highcharts. Following is the code snippet :
constructor() {
this.options = {
chart: { type: 'spline' },
title: { text : 'dynamic data example'},
series: [{ data: [2,3,5,8,13] }]
};
setTimeout(() => {
this.chart.series[0].setData([21,31,51,81,13]);
}, 3000);
setTimeout(() => {
this.chart.series.push({data:[]});
this.chart.series[1].setData([100,200,300]);
}, 5000);
}
Plunker link for the same .
Error :
ERROR TypeError: _this.chart.series[1].setData is not a function
at eval (run.plnkr.co/QZBbWi3BVy3DeOzT/app/main.ts!transpiled:28)
My Observation :
What is evident with that error is there is no setData function in the series array in the 1st element. Alternate approach to resolve this is, initializing this.options with empty object with data property :
constructor() {
this.options = {
chart: { type: 'spline' },
title: { text : 'dynamic data example'},
series: [{ data: [2,3,5,8,13] },
{ data: [] },
]
};
}
This is very crude way as i need to do the same for 50 elements in this.options.series if i see them coming dynamically
Is there any efficient and better approach to resolve this error ?
PS : Am new to Highcharts, so please bear with me if am doing something completely wrong at basic level.
setData modifies a series which already exists - if you do not have the series you cannot modify it.
If you want to add a new series, you should use chart.addSeries() method like this:
setTimeout(() => {
this.chart.addSeries({
data: [21,31,51,81,13]
});
}, 5000);
example: http://plnkr.co/edit/61IgH8t3YKjtIOgzpUPB?p=preview

How to update global variable after http call Angular

I have a global variable which I need to update after http get call. After update I need to pass this updated variable to other function. I can't understand what is the best way to do this. Here is my code:
app.controller('myCtrl', function($scope, $http) {
var data = '';
$http.get("data.json")
.then(function(response) {
data = response.data;
});
vm.mapOptions = {
controls: {
navigator: false
},
center: [40, -35],
zoom: 3,
layers: [{
style: {
fill: {
color: '#1996E4'
},
stroke: {
color: '#FFFFFF'
}
},
type: 'shape',
dataSource: data
}],
shapeCreated: onShapeCreated,
shapeFeatureCreated: onShapeFeatureCreated
};
});
Is it possible at all to update global variable after http call?
Thank you for help in advance.
When you do a http request, it takes some time to send the request and get the response, specially when you send the request to a API on a server,but in meanwhile the execution continues immediately and the statement after your http call is executed and if you have something that depends on the response, most properly will be failed.
in your case vm.mapOptions have dependency to data, which is a local variable getting the respnse from get request. so what can you do?
Step 1 :
make a function for all codes that are involve with your response :
$scope.myCallBack = function(data){
vm.mapOptions = {
controls: {
navigator: false
},
center: [40, -35],
zoom: 3,
layers: [{
style: {
fill: {
color: '#1996E4'
},
stroke: {
color: '#FFFFFF'
}
},
type: 'shape',
dataSource: data
}],
shapeCreated: onShapeCreated,
shapeFeatureCreated: onShapeFeatureCreated
};
}
Step 2 :
Call the myCallBack function inside your $http.get right after getting the response
var data = '';
$http.get("data.json")
.then(function(response) {
data = response.data;
$scope.myCallBack(data);
});

Categories

Resources