I want to use the results from API and render them into Angular highcharts.
However, I am unable to set the data accordingly. Below is the error that was thrown on console.
cpm.component.ts:91 ERROR Error: Uncaught (in promise): TypeError: Cannot set properties of undefined (setting 'data')
TypeError: Cannot set properties of undefined (setting 'data').
async ngOnInit(): Promise<void> {
console.log(this.moves)
console.log(this.moves[1]['Moves'])
}
getMoves() {
this.DataService.getMovesFromDB().subscribe(movesArray =>
movesArray.forEach(move => this.moves.push(move)))
return this.moves
}
highcharts = Highcharts;
chartOptionsTest = {
chart: {
type: "spline"
},
title: {
text: "Simulated VS Actual Move"
},
xAxis:{
categories:["Jan", "Feb", "Mar", "Apr", "May", "Jun",
"Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
},
yAxis: {
title:{
text:"Moves"
}
},
tooltip: {
valueSuffix:""
},
series: [
{
name: 'Before',
data: [1000, 5, 5, 5, 1000, 5, 5, 5, 1000, 5, 5, 5]
},
{
name: 'After',
data: this.moves[1]['Moves']
// Error is thrown
}
]
};
Both console.log(this.moves) and console.log(this.moves[1]['Moves']) did show result, for instance, console.log(this.moves[1]['Moves']) shows 23352546, which is the number from this.moves.
May I know what I have done wrongly?
Nope, you can't map the value from this.moves[1]['Moves'] directly in the variable declaration.
You should assign the value to the chartOptionsTest when the Observable is returned (API response returned).
Assumption:
Assume that you call the getMoves method in ngOnInit method.
async ngOnInit(): Promise<void> {
this.getMoves();
}
Declare the chartOptionsTest variable but remove the second item of series array which you set the value with this.moves[1]['Moves'].
chartOptionsTest: any = {
chart: {
type: 'spline',
},
title: {
text: 'Simulated VS Actual Move',
},
xAxis: {
categories: [
'Jan',
'Feb',
'Mar',
'Apr',
'May',
'Jun',
'Jul',
'Aug',
'Sep',
'Oct',
'Nov',
'Dec',
],
},
yAxis: {
title: {
text: 'Moves',
},
},
tooltip: {
valueSuffix: '',
},
series: [
{
name: 'Before',
data: [1000, 5, 5, 5, 1000, 5, 5, 5, 1000, 5, 5, 5],
},
],
};
In the subscribe method which is triggered when the observable (response) is returned, append the value this.moves[1]['Moves'] to the this.chartOptionsTest.series array with Array.push().
getMoves() {
this.DataService.getMovesFromDB().subscribe((movesArray) => {
movesArray.forEach((move) => this.moves.push(move));
this.chartOptionsTest.series.push({
name: 'After',
data: this.moves[1]['Moves'],
});
});
}
While this line
return this.moves;
can be removed as it is unnecessary for me and also it doesn't guarantee to return value if the Observable is not returned and the above code (subscribe part is not triggered to bind the value to this.moves).
Demo # StackBlitz
Related
My question is an extension of a previous one (enter link description here).
I remove the labels with null associated datas but now I'd like to remove space when this is the case (I have white spaces ath the left in this example).
Have I to deal with min and max ticks options?
<canvas id="myChart" width="200" height="200"></canvas>
<script>
var ctx = document.getElementById('myChart').getContext('2d');
let obj = {
labels: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
datasets: [{
spangaps: true,
label: 'Exceptionnel',
data: [1, 2, 3, 4, 5, 6, null, 5, null, null, null, null]
}]
};
var myChart = new Chart(ctx, {
type: 'bar',
data: obj,
options: {
scales: {
xAxes: [{
offset: true,
gridLines: {
display: true
},
ticks: {
callback: function (value, index, values) {
var dataValue = obj.datasets[0].data;
if (dataValue[index]) {
return values[index];
}
}
}
}]
}
}
});
</script>
What you consider a whites space at the left of your example is actually an invisible bar of value 1. This can be solved by defining option yAxes.ticks.beginAtZero: true or alternatively also yAxes.ticks.min: 0.
yAxes: [{
ticks: {
beginAtZero: true
}
}],
In case you want to remove leading null entries from your data, this can be done as follows. The Array.shift() method removes the first element from an array.
while(data[0] == null) {
data.shift(); // remove leading null
labels.shift(); // remove corresponding label
}
To also remove the tailing null entries from your data, you can proceed as follows. The Array.pop() method removes the last element from an array.
while(data[data.length-1] == null) {
data.pop(); // remove tailing null
labels.pop(); // remove corresponding label
}
The important thing is to always also remove the corresponding entry from labels, each time you remove an entry from data.
Please have a look at your amended code below.
const labels = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"];
const data = [1, 2, 3, 4, 5, 6, null, 5, null, null, null, null];
while(data[0] == null) {
data.shift(); // remove leading null
labels.shift(); // remove corresponding label
}
while(data[data.length-1] == null) {
data.pop(); // remove tailing null
labels.pop(); // remove corresponding label
}
var myChart = new Chart('myChart', {
type: 'bar',
data: {
labels: labels,
datasets: [{
label: 'Exceptionnel',
data: data
}]
},
options: {
scales: {
yAxes: [{
ticks: {
beginAtZero: true
}
}],
xAxes: [{
offset: true,
gridLines: {
display: true
}
}]
}
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.9.3/Chart.min.js"></script>
<canvas id="myChart" height="90"></canvas>
I create a ApexChart with empty values because I dynamically add series and labels.
var options777 = {
chart: {
height: 397,
type: 'line',
toolbar: {
show: false,
}
},
series: [{
name: 'Elegibility',
type: 'column',
data: []
}],
stroke: {
width: [0, 4]
},
// labels: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
labels: [],
xaxis: {
type: 'datetime'
},
yaxis: [{
title: {
text: 'Elegibility',
}
}
};
var chart777 = new ApexCharts(
document.querySelector("#elegibilityChart"),
options777
);
if (document.getElementById('elegibilityChart')) {
chart777.render();
}
Then, I have a function that receives two arrays, one with the series data and other with label data. The arrays are same size, series contains numbers and labels strings.
function setData(mySeries, myLabels) {
chart777.updateOptions({
series: {
data: mySeries
},
labels: myLabels
})
}
When I run my code, it returns: error TypeError: "options$$1.series[0] is undefined"
How can I update the series and labels in the ApexChart?
Thanks
If you're updating both the series and the labels, this will need to be done in two separate calls.
Use the updateSeries method to provide the new data series (as an array).
Use the updateOptions method to provide the new labels (as an object that will merge with the existing options, keeping any configurations not overridden).
chart1.updateOptions({
xaxis: {
categories: year
},
series: [{
data: trend
}],
});
You should use code like this, I mean you should update xaxis categories instead of labels and also comment labels in options and instead use xaxis categories.
In Highcharts, you have the ability to add responsive rules to your chart. This can be used in a similar fashion to mediaqueries, where you adjust options for differing screen sizes.
My issue is that the responsive chart option for height is not being applied on initial loading of the chart. I have provided this jsfiddle to demonstrate the issue. Interestingly, the other responsive rules are being applied. You can see in the jsfiddle that the labels on the xAxis are being shortened to single characters as expected. If you reflow the chart with the button (or resize the view) the chart will get the height rule. Additionally, when the height is set in pixels instead, the rule applies completely fine. Having a set height in pixels is not the responsive behavior I need, however.
Below is the same code from the jsfiddle because I have to put something when I link to jsfiddles. Is there something I'm missing here or this a bug that needs submitted to Highcharts? While calling reflow() each time works, it is repainting the chart an extra time and I can't be certain that won't cause an issue with a dynamic layout.
var chart = Highcharts.chart('container', {
chart: {
type: 'column'
},
title: {
text: 'Highcharts responsive chart'
},
subtitle: {
text: 'Resize the frame to see the axes change'
},
xAxis: {
categories: ['January', 'February', 'March', 'April', 'May', 'June',
'July', 'August', 'September', 'October', 'November', 'December']
},
yAxis: {
labels: {
x: -15
},
title: {
text: 'Items'
}
},
series: [{
name: 'Sales',
data: [434, 523, 345, 785, 565, 843, 726, 590, 665, 434, 312, 432]
}],
responsive: {
rules: [{
condition: {
maxWidth: 500
},
// Make the labels less space demanding on mobile
chartOptions: {
chart: {
height: "1000%"
},
xAxis: {
labels: {
formatter: function () {
return this.value.charAt(0);
}
}
},
yAxis: {
labels: {
align: 'left',
x: 0,
y: -2
},
title: {
text: ''
}
}
}
}]
}
});
$('#reflow').click(function () {
chart.reflow();
});
This is example for the simple column chart
$("#container").highcharts({
chart: {
type: 'column'
},
title: {
text: 'Monthly Average Rainfall'
},
subtitle: {
text: 'Source: WorldClimate.com'
},
xAxis: {
categories: [
'Jan',
'Feb',
'Mar',
'Apr',
'May',
'Jun',
'Jul',
'Aug',
'Sep',
'Oct',
'Nov',
'Dec'
],
crosshair: true
},
yAxis: {
min: 0,
title: {
text: 'Rainfall (mm)'
}
},
tooltip: {
headerFormat: '<span style="font-size:10px">{point.key}</span><table>',
pointFormat: '<tr><td style="color:{series.color};padding:0">{series.name}: </td>' +
'<td style="padding:0"><b>{point.y:.1f} mm</b></td></tr>',
footerFormat: '</table>',
shared: true,
useHTML: true
},
plotOptions: {
column: {
pointPadding: 0.2,
borderWidth: 0
}
},
series: [{
name: 'Tokyo',
data: [49.9, 71.5, 106.4, 129.2, 144.0, 176.0, 135.6, 148.5, 216.4, 194.1, 95.6, 54.4]
}]
});
I'm dynamically adding columns (points) like this
var chart = $("#container").highcharts();
chart.series[0].addPoint(["abc", 100]]);
So now I have to remove a column from the chart. I have dynamically added points by their names. So when I remove any particular added series name I need to remove that column from the chart.
When I add a new column (point) I create a button with its name with an 'x' mark so clicking that 'x' mark should remove that particular column from the chart. So all I have is its category name by which I need to remove it.
So I need to dynamically remove the column from the chart. Since it is all just a single series. I want the category to be removed with its respective data value. I'm not sure how to do that!
For example in the chart above I want to specifically remove 'Oct' from categories and its respective value from data '194.1'. And I just have its category value ('Oct'). Is that possible ?
I can't use this - chart.series[0].data[9].remove() because I don't know which index i'll have to remove since I only have its category value 'Oct'. Also it just removes the point from the chart and not the column as a whole (including its respective category value and reshifting the graph).
Does anyone know how I can do this?
You can create a function which filters your data and your categories and updated your chart.
Example:
function removeCategoryAndData (chart, categoryName) {
const { categories } = chart.xAxis[0]
const categoryIndex = categories.indexOf(categoryName)
const { data } = chart.series[0]
// Filter data and categories
const filteredData = data.map((p) => p.y).filter((v, i) => i !== categoryIndex)
const filterdCategories = categories.filter((v, i) => i !== categoryIndex)
// Update chart with filtered data and categories
chart.update({ xAxis: { categories: filterdCategories }, series: { data: filteredData } })
}
Live example:
https://jsfiddle.net/yy9bhvq2/
$http.post(galileoServer + "actions.php", {
"action": "get-attendance-graph",
"user": window.localStorage.getItem("username")
}).success(function(result){
//console.log(result)
busyIndicator("hide");
$('#attendance-graph').highcharts({
credits: 0,
tooltip:{
enabled: false
},
chart: {
type: 'bar'
},
title: {
text: '',
style: {
display: "none"
}
},
xAxis: {
categories: ['June', 'July', 'Aug', 'Sept', 'Oct', 'Nov', 'Dec', 'Jan', 'Feb', 'Mar', 'Apr', 'May']
},
yAxis: {
min: 0,
title: {
text: 'No. of days'
},
gridLineWidth: 0,
minorGridLineWidth: 0,
labels: { //to disable points displayed
enabled: false
}
},
legend: {
reversed: true
},
plotOptions: {
series: {
stacking: 'normal',
dataLabels: {
enabled: true,
color: (Highcharts.theme && Highcharts.theme.dataLabelsColor) || 'white',
formatter: function() { //function to avoid displaying zero values
if (this.y != 0) {
return this.y;
} else {
return null;
}
}
/*style: {
textShadow: '0 0 3px black, 0 0 3px black'
}*/
}
}
},
series: [{
name: 'Absent',
data: [result.absentData]
}, {
name: 'Present',
data: [result.presentData]
}]
});
}).error(function(result, status){
alert(status + "\nCouldn't connect to Galileo server due to network problem")
});
I am trying to load the data via ajax but the graph doesn't gets loaded the graph loaded is blank.
The coding snippet is provided.
I also tried with the getJSON part, but it also didn't worked.
Kindly let me know the solution, as I'm unable to get the graph since last two days.
The console output is {"absentData":"0,0,2,0,0,1,0,0,0,0,0,0","presentData":"30,31,29,30,31,29,31,31,28,31,30,31"}
Your json is not properly formed for Highcharts. You want an array of numbers, what you are giving it is an array of one element a string:
data: ["0,0,2,0,0,1,0,0,0,0,0,0"] // an array of a single string...
It's better that you fix this in your PHP code. You'll need to build a php array of ints (do not build a concatenated string) and then use json_encode.
If you can't fix it in the PHP, you could do something like:
data: $.parseJSON("["+result.absentData+"]")
but this is somewhat ugly.