High Charts fails to add series dynamically - javascript

I am using HighCharts for the first time and it's proving to be difficult to find sources to help me with this problem.
I am trying to display multiple charts with different numbers of series. Rather than set up multiple options templates I want to use just one and use a function to customize the options object each time. Here I've provided mock data for what one of the charts will look like as received from the API. In the application I plan on feeding it from HTML.
I keep running into various problems due to my lack of understanding of HighCharts, right now I am just going around in circles fixing one problem only for a past problem to pop-up again I was hoping someone can point me in the right direction.
This is my Class:
export class CompletenessTabComponent implements OnInit, AfterViewInit {
#ViewChild('chartTarget') chartTarget: ElementRef;
chart: Highcharts.ChartObject;
dataObj = {"id":0,
"tableName":"d1 vs d2",
"data":{"d1Count":[50,45,55,60,70],
"d2Count":[40,32,55,58,33],
"Errors":[2,3,4,1,0]},
"dates":[20180927,20180928,20181001,20181002,20181003]
};
constructor() { }
setHighChartOptions(data, seriesNames, chartTitle ): any {
count = 1;
highChartOptions.title.text = chartTitle;
highChartOptions.xAxis.data = data.dates;
this.chart.series[0].setData(this.dataObj.data[0]);
Object.keys(data.data).forEach((key) =>
this.chart.addSeries({
name: seriesNames[count],
data: data.data[key],
type: 'line'
}) count ++
);
return highChartOptions;
}
getHighChartOptions(){
return highChartOptions;
}
ngOnInit() {
this.chart = chart(this.chartTarget.nativeElement, this.setHighChartOptions(this.dataObj, ['d1', 'd2', 'error'], this.dataObj.tableName));
Object.keys(this.dataObj.data).forEach(key =>
console.log(key, this.dataObj.data[key]))
}
ngAfterViewInit() {
}
}
const highChartOptions = {
colors:
['#058DC7', '#50B432', '#ED561B', '#DDDF00', '#24CBE5', '#64E572', '#FF9655', '#FFF263', '#6AF9C4'],
credits: {
enabled: false
},
chart: {
backgroundColor: null,
},
legend: {
layout: 'vertical',
align: 'right',
verticalAlign: 'top',
itemStyle: {
fontSize: '10px',
fontWeight: 'normal'
}
},
tooltip: {
valuePrefix: '$'
},
title: {
text: ''
},
xAxis: {
type: 'date',
data: [],
title: 'date'
},
yAxis: {
title: {
align: 'middle',
rotation: 0,
text: ''
}
},
plotOptions: {
series: {
marker: {
enabled: false
},
shadow: false
}
},
series: [{
type: 'line',
name: '',
data: [null]
}]
};
A few errors I keep receiving are:
in setHighChartOptions() : Cannot read property 'series' of undefined (at, this.chart.series[0]), & cannot read 'keys' of undefined (at the start of the ForEach loop).
I believe the big issue is the chart object is undefined which doesn't make sense since I've instantiated it at the top of the class and then onInit I set the chart options.
I hope you will be able spot my errors and assist me. Thank You.

Most probably, it's because you're trying to refer to series which is not already defined. Actually, if you calling the setHighchartsOptions for a first time, your chart is not defined yet.
In order to make it works, please try to init the chart first (even could be the empty chart, without any data), and then call another methods on Chart object, like addSeries. Something like that, but i didn't saw whole your app, so it's hard to write completely adjusted solution.
ngOnInit() {
this.chart = chart(this.chartTarget.nativeElement, {});
this.chart.update(this.setHighChartOptions(this.dataObj, ['d1', 'd2', 'error'], this.dataObj.tableName))
Object.keys(this.dataObj.data).forEach(key =>
console.log(key, this.dataObj.data[key]))
}

First of all, you must be careful to use import correctly all js package in your Angular project:
import { chart } from 'highcharts';
import * as Highcharts from 'highcharts';
Also, you must check does your package installed correctly with "npm"?
After all, As my point of view, this example can help you to better understand how to use "highcharts" package in your project, other configs depend on the version of "highcharts" you are using it.
import { Component, ElementRef, ViewChild } from '#angular/core';
import { chart } from 'highcharts';
import * as Highcharts from 'highcharts';
#Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent {
title = 'Highcharts Sample';
#ViewChild('chartTarget') chartTarget: ElementRef;
chart: Highcharts.ChartObject;
ngAfterViewInit() {
const options: Highcharts.Options = {
chart: {
type: 'bar'
},
title: {
text: 'Sample Title'
},
xAxis: {
categories: ['Top Test', 'Test 2', 'Test3']
},
yAxis: {
title: {
text: 'Fox Rex'
}
},
series: [{
name: 'Tomi',
data: [1, 0, 4]
}, {
name: 'Alex',
data: [5, 7, 3]
}]
};
this.chart = chart(this.chartTarget.nativeElement, options);
}
addSeries(){
this.chart.addSeries({
name:'Test',
data:[2,3,7]
})
}
}
And HTML template of sample:
<div #chartTarget>
chart target sample
</div>
Also, you must serve data to highcharts as the format is standard for that, check JSON sample data format in examples in highcharts website.

Related

Is there a way to implement chart-js-plugin-waterfall as a react component

I'm facing a situation here, including React use coupled with the additionnal plugin from the chart.js library : chartjs-plugin-waterfall.
I have to develop a personalised dashboard for my client, and until now I've been able to implement every type of charts needed with the library chartjs and its react components : react-chartjs-2.
But I'm now struggling for hours with the last one I have to implement -and which is not available as a "react-chart component"- : the Waterfall chart.
For this I decided to add chartjs-plugin-waterfall and to implement it as a new Chart (here a code sample) :
const WaterFallType = () => {
let chart = false
useEffect(() => {
if(chart instanceof Chart) {
chart.destroy();
}
let ctx = document.getElementById("myChart").getContext('2d')
chart = new Chart(ctx, {
type: "bar",
plugins: [waterFallPlugin],
data :{
datasets: [
{
label: 'Closing Costs',
data: [50],
backgroundColor: '#e8cdd7',
stack: 'stack 1',
},
{
label: 'Purchase Price',
data: [700],
backgroundColor: '#d29baf',
stack: 'stack 1',
},
{
data: [200],
waterfall: {
dummyStack: true,
},
stack: 'stack 2',
},
{
label: 'Opening Loan Balance',
data: [550],
backgroundColor: '#bb6987',
stack: 'stack 2',
},
{
label: 'Initial Cash Investment',
data: [200],
backgroundColor: '#a53860',
stack: 'stack 3',
},
],
},
options: {
plugins: {
waterFallPlugin: {
stepLines: {
enabled: true,
startColorStop: 0,
endColorStop: 0.6,
startColor: 'rgba(0, 0, 0, 0.55)',
endColor: 'rgba(0, 0, 0, 0)',
diagonalStepLines: true,
},
},
},
}
})
}, [chart])
return <div>
<canvas id="myChart" width="400px" height="400px"></canvas>
</div>
}
export default WaterFallType
Unfortunatly This code does not render the component, instead it throws me several errors :
First one : "Uncaught Error: Canvas is already in use. Chart with ID '2' must be destroyed before the canvas can be reused."
Second one : "Uncaught TypeError: Cannot set properties of undefined (setting 'filter')"
Could anyone help me with this battle?
Thank you very much for your help.
Benjamin
Finally found a solution without any plugin need.
Chart.js allows floating bars :
https://www.chartjs.org/docs/3.2.1/samples/bar/floating.html
Which makes it a lot more easier!
Have an excellent day!

Highcharts Gauge not showing in Angular

I am using Angular and Higcharts to visualize some data. I have gotten many of the other Highchart components working, now I want to use the gauges. I went to the Highcharts website and used the example at their demo section. Unfortunately this is not working.
For some reason the gauge is blank but the other parts of my component shows up. Example below:
My code looks like this:
TS file
import { Component, OnInit } from '#angular/core';
import * as Highcharts from 'highcharts';
#Component({
selector: 'app-speedo',
templateUrl: './speedo.component.html',
styleUrls: ['./speedo.component.scss']
})
export class SpeedoComponent implements OnInit {
chartOptions: any;
Highcharts: typeof Highcharts = Highcharts;
chartRef: Highcharts.Chart;
updateFromInput = false;
constructor() { }
ngOnInit(): void {
this.myChart();
}
chartCallback: Highcharts.ChartCallbackFunction = (chart) => {
this.chartRef = chart;
};
myChart(){
this.chartOptions = {
chart: {
type: 'solidgauge'
},
title: null,
pane: {
center: ['50%', '85%'],
size: '140%',
startAngle: -90,
endAngle: 90,
background: {
backgroundColor:
Highcharts.defaultOptions.legend.backgroundColor || '#EEE',
innerRadius: '60%',
outerRadius: '100%',
shape: 'arc'
}
},
exporting: {
enabled: false
},
tooltip: {
enabled: false
},
// the value axis
yAxis: {
stops: [
[0.1, '#55BF3B'], // green
[0.5, '#DDDF0D'], // yellow
[0.9, '#DF5353'] // red
],
lineWidth: 0,
tickWidth: 0,
minorTickInterval: null,
tickAmount: 2,
title: {
y: -70
},
labels: {
y: 16
}
},
plotOptions: {
solidgauge: {
dataLabels: {
y: 5,
borderWidth: 0,
useHTML: true
}
}
}
}
setTimeout(() => {
window.dispatchEvent(
new Event('resize')
);
}, 300);
}
}
HTML file
<p>speedo works!</p>
<highcharts-chart
[Highcharts]="Highcharts"
[options]="chartOptions"
style="width: 100%; height: 400px; display: block;"
[(update)]="updateFromInput"
[callbackFunction]="chartCallback">
</highcharts-chart>
Does anyone know what is wrong with this code. How can I fix it so that the gauge actually shows up?
If anyone can recommend any other package I can use to show real time data on a gauge I would appreciate it. This package should be easy to use, or at least have more resources than Highcharts where one can find tutorials an so on.
Thanks in advance!
There are a few glitches:
The highcharts-more module is missing. Import it at the begging
import HC_more from "highcharts/highcharts-more";
HC_more(Highcharts);
Your demo was not working simply because there was nothing to show.
All you have to do to fix this is to set the data in the series object.
Remember to always add the series type for each series.
series: [
{
type: "gauge",
data: [80]
}
]
Also, I highly recommend Highhcarts official documentation where you might find more examples and descriptions.
API:
https://api.highcharts.com/highcharts/series.gauge
https://github.com/highcharts/highcharts-angular
Demo:
https://stackblitz.com/edit/highcharts-angular-gauge

VueJs highstock cant draw graph properly

This is my VueJS code, i use this lib https://www.npmjs.com/package/highcharts-vue
So i dont know how i can get data and set it to series before the graph is drawn. Or if this is not posible, how can i redraw graph properly? Becouse now i set some default data, then get data from page, and redraw graph, but when its done and i see my graph, the scrollbar go to the left side and has a very small range. So how set options without change scrollbar and range selector?
<template>
<highcharts :constructor-type="'stockChart'" :options="options" :updateArgs="[true, false]" ref="linerchart"></highcharts>
</template>
<script>
import {Chart} from 'highcharts-vue'
import Highcharts from 'highcharts'
import stockInit from 'highcharts/modules/stock'
stockInit(Highcharts)
export default {
data: () => ({
obj: [],
names: ['CAFF'],
options: {
credits: { enabled: false },
rangeSelector: {
selected: 1,
inputEnabled: false,
buttonTheme: {
visibility: 'visible'
}
},
yAxis: {
labels: {
formatter: function () {
return (this.value > 0 ? ' + ' : '') + this.value + '%';
}
},
plotLines: [{
value: 0,
width: 2,
color: 'silver'
}]
},
plotOptions: {
series: {
compare: 'percent',
showCheckbox: false,
events: {
checkboxClick: function (event) {
if (event.checked) {
this.show();
} else {
this.hide();
}
}
}
}
},
tooltip: {
pointFormat: '<span style="color:{series.color}">{series.name}</span>: <b>{point.y}</b> USD<br/>',
split: true
},
series: [{
name: "CAFF",
data: [1,2,3,4,5,6]
}]
}
}),
methods: {
linerGraph() {
var that = this;
this.names.forEach(function(name, i){
axios.get('/test/account/CAFF')
.then(response => {
console.log(response.data)
that.obj.push({name: name, data: response.data});
});
});
this.options.series = that.obj
},
},
components: {
highcharts: Chart
},
mounted() {
console.log(this.$refs.linerchart)
this.linerGraph();
}
}
</script>
You should use other VueJS lifecycle hook point to run axios request, because you are trying to download it when the component is already mounted(), so please try to use one of hook points before that one, e.g created().
Here is the Lifecycle diagram from Vue General Documentation:

Highmaps chart is empty in Angular 5

I have a component where I have to show high maps. No errors but the maps is always empty
My chart options object :
let chartData = [{ code3: "ABW", z: 105 }, { code3: "AFG", z: 35530 }];
this.chartConfigObject = new MapChart(<any>{
chart: {
borderWidth: 1,
map: 'custom/world'
},
title: {
text: 'World population 2013 by country'
},
subtitle: {
text: 'Demo of Highcharts map with bubbles'
},
legend: {
enabled: false
},
mapNavigation: {
enabled: true,
buttonOptions: {
verticalAlign: 'bottom'
}
},
series: [{
name: 'Countries',
color: '#E0E0E0',
enableMouseTracking: false
}, {
type: 'mapbubble',
name: 'Population 2016',
joinBy: ['iso-a3', 'code3'],
data: chartData,
map: mapData,
minSize: 4,
maxSize: '12%',
tooltip: {
pointFormat: '{point.properties.hc-a2}: {point.z} thousands'
}
}]
});
When I console the created MapChart object i am getting the series as null always.
What am i missing and why the maps series is always null?
I am using angular-highcharts component as explained here :
https://github.com/cebor/angular-highcharts
reproduced in stackblitz: https://stackblitz.com/edit/angular-highcharts-stock-nbvnuw?file=app%2Fapp.module.ts
You are not providing the map data to your chart , so it doesn't know which map to show. Try showing the map data like mapData: Highcharts.maps['custom/world'] under Series.
If you cannot access the maps in your Highcharts reference , try importing all the maps (JS files) from the HighMaps library into your project and reference them in your typescript component file like this:
const Highcharts = {maps: {}};
require('../../../assets/maps')(Highcharts); //your custom project path
and use mapData: Highcharts['maps']['custom/world'] or whatever is the map you wanted to use like 'countries/us/us-all'
Below is working map code using angular-highcharts to plot the map and points on it:
getMap(data) {
return new MapChart({
chart: {
events: {
load: function (point) {
//do something here on page load
}
},
spacingBottom: 20
backgroundColor: '#FFF',
style: {fontFamily: 'inherit'}
},
title: {
text: ''
},
mapNavigation: {
enabled: true
},
tooltip: {
headerFormat: '',
formatter: function () {
var toolTipTxt = '<b>' + this.point.options.name + '</b>';
return toolTipTxt;
}
},
plotOptions: {
series: {
color: '#00A778',
marker: {
radius: 6,
symbol: 'circle',
lineColor: '#00A778',
lineWidth: 1,
states: {
select: {
fillColor: '#00A778',
lineWidth: 1,
lineColor: '#00A778'
}
}
},
point: {
events: {
click: function () {
this.select(null, true);
}
}
}
}
},
series: [
{
//mapData: Highcharts['maps']['custom/world']
mapData: Highcharts.maps['custom/world'],
name: 'Basemap',
borderColor: '#A0A0A0',
nullColor: 'rgba(200, 200, 200, 0.3)',
showInLegend: false,
data: []
},
{
// Specify points using lat/lon
type: 'mappoint',
name: 'MySites',
data: data
}
]
});
}
Also you need proj4js library to plot points on the map, in case if that's your requirement as well.
Hope this helps !
Might, it can be useful.
Here is the solution for angular-highcharts:
1) Download the map from collection:
https://code.highcharts.com/mapdata/
2) Create a variable that equals to the content of this file and export it:
instead of
Highcharts.maps["custom/world"] = { ... content of the file ... }
use
const worldMap = { ... content of the file ... }
export default worldMap;
2) Import this map to your component (for world-map.ts):
import worldMap from './world-map';
3) use
chart: {
...
map: worldMap,
...
},
And, of course, don't forget to add a couple of lines to your module:
import { ChartModule, HIGHCHARTS_MODULES } from 'angular-highcharts';
import * as highmaps from 'highcharts/modules/map.src';
#NgModule({
...
providers: [
{ provide: HIGHCHARTS_MODULES, useFactory: () => [highmaps] }
],
...
})
About modules: https://www.npmjs.com/package/angular-highcharts#highmaps
Here is the working example:
https://stackblitz.com/edit/angular-highcharts-stock-byjo6a
Solution was found here:
https://forum.highcharts.com/highmaps-usage-f14/highmaps-bubble-chart-is-empty-t40432/

How to implement Highcharts column-drilldown chart in Rails application?

I'm trying to implement a Highcharts Column-Drilldown Chart in my Rails application using lazy_high_charts. I want to display data that's being pulled from my database and stored in four arrays (areas, areaScores, departments, and deptScores). I'm having trouble converting the JS from the example (JSFiddle) listed on the highchart site into ruby. I have not been able to find any resources on creating a column-drilldown chart in ruby. Any help on how to integrate the drilldown chart into my ruby application would be highly appreciated.
I have included the sample JavaScript shown on the Highcharts demo page and my controller method that populates the four arrays with data and builds the highchart.
Highcharts Column-Drilldown Chart Example (Javascript)
$(function () {
Highcharts.data({
csv: document.getElementById('tsv').innerHTML,
itemDelimiter: '\t',
parsed: function (columns) {
var brands = {},
brandsData = [],
versions = {},
drilldownSeries = [];
// Parse percentage strings
columns[1] = $.map(columns[1], function (value) {
if (value.indexOf('%') === value.length - 1) {
value = parseFloat(value);
}
return value;
});
$.each(columns[0], function (i, name) {
var brand,
version;
if (i > 0) {
// Remove special edition notes
name = name.split(' -')[0];
// Split into brand and version
version = name.match(/([0-9]+[\.0-9x]*)/);
if (version) {
version = version[0];
}
brand = name.replace(version, '');
// Create the main data
if (!brands[brand]) {
brands[brand] = columns[1][i];
} else {
brands[brand] += columns[1][i];
}
// Create the version data
if (version !== null) {
if (!versions[brand]) {
versions[brand] = [];
}
versions[brand].push(['v' + version, columns[1][i]]);
}
}
});
$.each(brands, function (name, y) {
brandsData.push({
name: name,
y: y,
drilldown: versions[name] ? name : null
});
});
$.each(versions, function (key, value) {
drilldownSeries.push({
name: key,
id: key,
data: value
});
});
// Create the chart
$('#container').highcharts({
chart: {
type: 'column'
},
title: {
text: 'Browser market shares. November, 2013'
},
subtitle: {
text: 'Click the columns to view versions. Source: netmarketshare.com.'
},
xAxis: {
type: 'category'
},
yAxis: {
title: {
text: 'Total percent market share'
}
},
legend: {
enabled: false
},
plotOptions: {
series: {
borderWidth: 0,
dataLabels: {
enabled: true,
format: '{point.y:.1f}%'
}
}
},
tooltip: {
headerFormat: '<span style="font-size:11px">{series.name}</span><br>',
pointFormat: '<span style="color:{point.color}">{point.name}</span>: <b>{point.y:.2f}%</b> of total<br/>'
},
series: [{
name: 'Brands',
colorByPoint: true,
data: brandsData
}],
drilldown: {
series: drilldownSeries
}
})
}
});
});
My Controller:
def generateOrgBreakdownReport
# First, query the database for the data you need for the report
#jsonanswerBRKD = queryDatabaseForOrgProgressReport()
# Second, process and combine data as needed for the report
#areaBreakdown, #deptBreakdown, #employBreakdown = computeAreaAndDeptPrepareScore(#jsonanswerBRKD)
# Third, you'll need to put the processed data into a format
# Highcharts will understand for the data series it uses
# for the graph.
#THESE ARRAYS HOLD THE NAMES AND SCORES OF AREAS AND DEPARTMENTS
#deptScores, #departments, #areaScores, #areas = cycleThroughProcessedDataAndCreateHighChartsDataSetsBreakdown(#areaBreakdown, #deptBreakdown, #employBreakdown)
# Last, we put the newly made data sets for Highcharts to work its magic.
#DONT KNOW HOW TO IMPLEMENT DRILLDOWN FOR RUBY
#orgBreakdown = LazyHighCharts::HighChart.new('column') do |f|
f.chart( type: 'column' )
f.xAxis(
title: { text: "Areas" },
type: 'category'
)
f.yAxis(
title: { text: "Preparedness Score (%)"},
)
f.series(
name: "Department Score",
colorByPoint: true,
data: #deptScores
)
f.series(
name: "Area Score",
data: #areaScores
)
f.title(
text: "Organizational Breakdown"
)
f.options[:xAxis][:categories] = #areas
f.drilldown({:series=>{
name:"Dept. Score",
data: #deptScore
}
})
end
end
Thanks,
Matt
I haven't used Lazy Highcharts, but assuming it mirrors the JSON from the JavaScript API you need to add the sub-series by name, e.g.
f.series(
name: "Department Score",
colorByPoint: true,
data: #deptScores,
drilldown: "subdept" #add this
)
Then you'll need to add drilldown data, and if Lazy Highcharts supports it, it might look something like this:
f.drilldown(
series: {
id: "subdept",
data: [
["One", 1],
["Two", 2],
["Three", 3]
]
}
)
See this basic drilldown fiddle to see how the resulting Javascript should look.
To get drilldown to work in Rails you have to make sure you include the drilldown module in your JavaScript manifest file (application.js).
I also had to download the file as it was not my highcharts module catalogue. You can find the file here: http://code.highcharts.com/modules/drilldown.js
Add this to application.js:
//= require highcharts/modules/drilldown
Outside of Rails you can include the drilldown module like this:
<script src="http://code.highcharts.com/modules/drilldown.js"></script>

Categories

Resources