Highcharts error #13 when trying to create a map - javascript

I'm trying to create a Highcharts map but I can't get past the #13 error which states that the rendering container can't be found. All my other charts that's not a map chart works and it uses the same container class but I can't get maps to work at all.
Config object that gets passed to the initMapChart function (the map chart uses the same config as the normal charts):
highcharts.config = {
chart: {
type: highcharts.getTypeFromId(graph.type)
},
plotOptions: {
series: {
stickyTracking: false,
events: {
click: function (e) {
this.chart.myTooltip.refresh(e.point, e);
}
}
},
pie: {
cursor: 'pointer',
dataLabels: {
enabled: true,
formatter: function() {
return this.series.isLabelsVisible ? this.y : '';
}
},
showInLegend: true
},
line: {
cursor: 'pointer',
dataLabels: {
enabled: true
}
},
area: {
cursor: 'pointer',
dataLabels: {
enabled: true,
formatter: function() {
return this.series.isLabelsVisible ? this.y : '';
}
}
},
areaspline: {
cursor: 'pointer',
dataLabels: {
enabled: true,
formatter: function() {
return this.series.isLabelsVisible ? this.y : '';
}
}
},
column: {
stacking: false,
cursor: 'pointer',
dataLabels: {
enabled: true,
formatter: function() {
return this.series.isLabelsVisible ? this.y : '';
}
}
},
map: {
mapData: Highcharts.maps['custom/world'],
joinBy: ['name'],
cursor: 'pointer',
dataLabels: {
enabled: true
}
}
},
exporting: {
buttons: {
contextButton: {
enabled: false
}
}
},
toggleModify: false,
title: {
text: ''
},
xAxis: {
categories: null,
labels: {
format: graph.span === 0 && graph.isTime && graph.childTarget === null ? '{value:%Y-%m-%d}' : '{value}'
}
},
yAxis: {
title: {
text: ''
},
labels: {}
},
series: null,
credits: {
enabled: false
}
};
I wait for the series to be created first before I try creating the charts, here is where I also set the renderTo property:
scope.$on('highchartsConfigUpdated', function(e, config) {
initDataType();
bindDataTypes();
var target = element[0].querySelector('.highcharts-target');
config.chart.renderTo = target;
if (scope.graph.type === 6) {
initMapChart(config);
}
else {
initChart(config);
}
});
This function will be run for all charts with the type of 6, which means they're a map chart:
function initMapChart(config) {
console.log(config.chart.renderTo);
new Highcharts.Chart('Map', config, function(chart) {
initLabels(chart);
bindLabels(chart);
});
};
From the console log I get this which should mean that it can in fact find the element:
<div id="chart-b2b2d164-b41c-40fc-a739-00cb1bc074ba" class="highcharts-target"></div>
So I think the issue is rather in the instantiation of the chart itself, how do you create a map chart without using jQuery?

Related

Accessing another list in the dataLabels.formatter function in Highcharts

I want to use another condition in the dataLabels.formatter function to determine whether to show a label or not and this another condition requires accessing a new list (indicated by "this.something" below) which will have exactly the same number of elements as the "data" list.
series: [
{
name: 'Value',
data: [92.0, 92.0, 84.0],
dataLabels: {
enabled: true,
formatter: function() {
if (this.y > 90 && this.something == 1) { return this.y }
}
}
}
],
How can I do this? I tried something like the following but it didn't work:
series: [
{
name: 'Value',
data: [{
y:92.0,
something:0
}, {
y:92.0,
something:1
}, {
y:84.0,
something:0
}],
dataLabels: {
enabled: true,
formatter: function() {
if (this.y > 90 && this.something == 1) { return this.y }
}
}
}
],
I learned that I can use this.point.something to refer to the new field I added.
series: [
{
name: 'Value',
data: [{
y:92.0,
something:0
}, {
y:92.0,
something:1
}, {
y:84.0,
something:0
}],
dataLabels: {
enabled: true,
formatter: function() {
if (this.y > 90 && this.point.something == 1) { return this.y }
}
}
}
],

Generate javascript and html blocks on the fly

In my website, I'm currently using 3 csv files, that are rendered by highcharts with the following javascript:
$(document).ready(function() {
$.get('export_xxxx.csv', function(csv) {
$('#xxx').highcharts({
data: {
csv: csv
},
title: {
text: 'Twitter (#xxxxx)'
},
yAxis: {
allowDecimals: false,
title: {
text: 'Users'
}
},
series: [{
visible: true
}, {
visible: false
}],
plotOptions: {
line: {
dataLabels: {
enabled: true
}
}
},
});
});
});
$(document).ready(function() {
$.get('export_xxxxyy.csv', function(csv) {
$('#xxx').highcharts({
data: {
csv: csv
},
title: {
text: 'Twitter (#xxxx)'
},
yAxis: {
allowDecimals: false,
title: {
text: 'Users'
}
},
series: [{
visible: true
}, {
visible: false
}],
plotOptions: {
line: {
dataLabels: {
enabled: true
}
}
},
});
});
});
$(document).ready(function() {
$.get('export_xxx_.csv', function(csv) {
$('#xxx').highcharts({
data: {
csv: csv
},
title: {
text: 'Twitter (#xxx)'
},
yAxis: {
allowDecimals: false,
title: {
text: 'Users'
}
},
series: [{
visible: true
}, {
visible: false
}],
plotOptions: {
line: {
dataLabels: {
enabled: true
}
}
},
});
});
});
and 3 html lines (It's like a dashboard).
<div id="xxx" style="width: 49%; height: 400px; margin: 0 auto;float:left;"></div>
My wish is to craft an HTML page with javascript of highcharts, to handle as many as csv there is in the DocumentRoot of my website.
For example, if I put 10 csv files, the generic HTML file will display 10 different charts on the same page, without editing manually the HTML page and add javascript too. Generated on the fly, in function of how many csv file there is.
Thanks for your help,
Instead of Repetitive code you can wrap your common code into function and call when you need to render chart with data.
renderCharts function accept csv data and id value.
function renderCharts(data,key){
$('#' + key).highcharts({
data: {
csv: data
},
title: {
text: 'Twitter (#xxx)'
},
yAxis: {
allowDecimals: false,
title: {
text: 'Users'
}
},
series: [{
visible: true
}, {
visible: false
}],
plotOptions: {
line: {
dataLabels: {
enabled: true
}
}
}})
};
$(document).ready(function() {
var ids = ['export_xxx_.csv','export_xxxxyy.csv','export_xxxxyyddd.csv'];
var promises = [];
ids.forEach(function(id){
promises.push($.get(id));
});
$.when(promises).then(function(values){
values.forEach(function(value){
renderCharts(value,'xxx');
})
});
}
Assuming your webserver gives a JSON response for list of files, say like this,
[ '/data1.csv', /date2.csv', ... ]
You can use something like this
$.get('/data', function(listOfFiles) {
for (var i = 0; i < listOfFiles.length; i++) {
$('body').append('<div id="chart' + i + '" style="width: 49%; height: 400px; margin: 0 auto;float:left;"></div>');
$.get(listOfFiles[i], function(csv) {
$('#chart' + i).highcharts({
.....
});
});
}
});
Ideally though, you should have a API retrieving data from a
database instead of using files.

Creating Highcharts with Angular using Highcharts >= 5.0.0 and highcharts-ng >= 1.0.0 using a ChartFactory

I would like to make the transition from pre-version 5.0.0 Highcharts and highcharts-ng version 0.0.12 to the latest versions.
I know there are some critical changes in the chart object, as well as with how highcharts-ng works in the latest versions. I have been searching for an example on how to set my Angular environment up using these latest versions, but it seems there is a great shortage on resources using these versions.
I was hoping you could help me with setting this up.
I have tried numerous changes myself, but I keep getting errors I did not get with the older versions of the scripts. Errors such as:
"Unable to get property of 'series' of undefined or null reference
(at $doCheck
(http://localhost:50262/app/vendorScripts/highcharts-ng.js:51:16))",
"Cannot set property 'getChartObj' of undefined" (at HighChartNGController.$onInit (highcharts-ng.js:36)),
"Multiple definitions of a property not allowed in strict mode (ChartFactory)"
This is my working solution pre-latest versions:
Here is my ChartFactory:
'use strict';
angular.module('portalDashboardApp')
.factory('ChartFactory', function () {
return {
getChartConfig: function () {
return {
options: {
chart: {
type: 'bar',
height: 400,
spacingTop: 30,
spacingBottom: 10,
spacingLeft: 10,
spacingRight: 50,
zoomType: 'x',
backgroundColor: false,
resetZoomButton: {
position: {
x: 0,
y: 40
}
}
},
credits: {
enabled: false
},
navigation: {
buttonOptions: {
y: -30
}
},
tooltip: {
formatter: function () {
return '<b>' + this.y + ' ' + this.series.name + '</b>';
}
},
plotOptions: {
column: {
stacking: ''
},
pie: {
allowPointSelect: true,
cursor: 'pointer',
dataLabels: {
enabled: true,
},
showInLegend: true
},
series: {
animation: true,
point: {
events: {
click: function () {
}
}
},
dataLabels: {
enabled: true,
format: ''
}
}
},
exporting: {
sourceWidth: 1600,
sourceHeight: 800,
// scale: 2 (default)
chartOptions: {
subtitle: null
},
buttons: {
contextButton: {
text: 'Export Chart'
}
}
}
},
title: {
text: false
},
xAxis: {
type: 'category'
},
yAxis: {
gridLineWidth: 1,
title: {
text: 'Count'
},
labels:
{
enabled: true,
format: '{value}'
}
},
legend: {
layout: 'vertical',
floating: true,
backgroundColor: '#FFFFFF',
align: 'right',
verticalAlign: 'top',
y: 60,
x: -60
},
series: [{}],
loading: false
};
}
};
});
This is how I would create a chart in my Angular controller:
I would inject my ChartFactory
Get my data from mt API call
And then set up, customize and apply my data for my chart using these methods:
function volumeGraphConfig(timeline_data, time_trend) {
$scope.VolumeGraphChartConfig = ChartFactory.getChartConfig();
$scope.VolumeGraphChartConfig.chart.type = 'column';
}
function populateVolumeData(timeline_data, time_trend) {
$scope.VolumeGraphChartConfig.xAxis.categories = timeline_data;
$scope.VolumeGraphChartConfig.series = [{
name: 'Twitter',
data: time_trend.Twitter,
color: twitterColor
}, {
name: 'Instagram',
data: time_trend.Instagram,
color: instagramColor
}, {
name: 'Youtube',
data: time_trend.Youtube,
color: youtubeColor
}];
$scope.VolumeGraphChartConfig.tooltip = {
pointFormat: '<span style="color:{series.color}">{series.name}</span>: <strong>{point.y}</strong> mentions<br/>',
shared: false
};
}
I would then display my chart using this HTML div:
<highchart id="FacebookVolume" config="volumeGraphChartConfig"></highchart>
How can I change this to work with the latest versions? Thank you!
The first two errors:
Cannot set property 'getChartObj' of undefined
Unable to get property of 'series' of undefined or null reference
happen when your config object is undefined.
For your case, you need to use the same variable you declared on your $scope
<highchart id="FacebookVolume" config="VolumeGraphChartConfig"></highchart>
(capital V)

Highmaps - how i can have a seperate legend by different report?

I have two (or more) reports, i want to combine this two reports and show in one map by a separate legend.
Please see example.
Also the result shared in tooltip when legends are visible.
$(function () {
// Initiate the chart
$('#container').highcharts('Map', {
plotOptions : {
map : {
mapData: Highcharts.maps['countries/ir/ir-all'],
joinBy: 'hc-key',
states: {
hover: {
color: '#BADA55'
}
},
dataLabels: {
enabled: true,
format: '{point.name}'
}
}
},
title : {
text : 'Highmaps basic demo'
},
mapNavigation: {
enabled: true,
buttonOptions: {
verticalAlign: 'bottom'
}
},
series : [{
name : 'Report 1',
data : [{
'hc-key' : "ir-ea",
value : 1000,
},{
'hc-key' : "ir-kv",
value : 1000,
},{
'hc-key' : "ir-kd",
value : 1000,
},{
'hc-key' : "ir-wa",
value : 1000,
}],
mapData: Highcharts.maps['countries/ir/ir-all'],
joinBy: 'hc-key',
states: {
hover: {
color: '#BADA55'
}
},
dataLabels: {
enabled: true,
style : {
textShadow : '',
},
format: '<span style="color:black">{point.name}</span>',
}
},{
name : 'Report 2',
data : [{
'hc-key' : "ir-wa",
value : '3000',
},{
'hc-key' : "ir-ea",
value : '3000',
}],
mapData: Highcharts.maps['countries/ir/ir-all'],
joinBy: 'hc-key',
states: {
hover: {
color: '#BADA55'
}
},
dataLabels: {
enabled: true,
style : {
textShadow : '',
},
format: '<span style="color:black">{point.name}</span>',
}
}]
});
});
this is not end answer, only a better solution.
i added legendItemClick event for better view.
Please see example.
events : {
legendItemClick : function(){
for(i=0; i < this.chart.series.length; i++) {
this.chart.series[i].hide();
}
},
},
but i want share same province data. for example, i have value = 1 in Golestan province in report 1 and i have value = 3 in Golestan province in report 2. sum of result tooltip is :
Report 1
Golestan : 1
Report 2
Golestan : 3
Sum
Goelstan : 4
Try this. What I have missed so far is to make the tooltip appear even if there is no value for the currently selected report.
This code in jsfiddle. Is this what you are trying to establish?
$(function() {
// Initiate the chart
$('#container').highcharts('Map', {
plotOptions: {
map: {
mapData: Highcharts.maps['countries/ir/ir-all'],
joinBy: 'hc-key',
states: {
hover: {
color: '#BADA55'
}
},
dataLabels: {
enabled: true,
format: '{point.name}'
},
events: {
legendItemClick: function() {
for (i = 0; i < this.chart.series.length; i++) {
this.chart.series[i].hide();
}
},
},
}
},
title: {
text: 'Highmaps basic demo'
},
tooltip: {
formatter: function() {
var pointName = this.point.name;
function filterByName(value) {
return (value.hasOwnProperty("name") && typeof value.name !== "undefined" && value.name === pointName);
}
var result = "<b>" + this.point.name + "</b><br>";
var allSeries = this.series.chart.series;
var curSeries, curValue;
for (var i = 0; i < allSeries.length; i++) {
curSeries = allSeries[i];
curValue = curSeries.points.filter(filterByName);
if (curValue.length === 0 || (curValue[0].hasOwnProperty("value") && curValue[0].value == null)) {
return result;
}
curValue = curValue[0].value;
result += '<br><b>' + curSeries.name + '</b> ' + curValue;
}
return result;
}
},
mapNavigation: {
enabled: true,
buttonOptions: {
verticalAlign: 'bottom'
}
},
series: [{
name: 'Report 1',
visible: false,
data: [{
'hc-key': "ir-ea",
value: 1000,
}, {
'hc-key': "ir-kv",
value: 1000,
}, {
'hc-key': "ir-kd",
value: 1000,
}, {
'hc-key': "ir-wa",
value: 1000,
}],
mapData: Highcharts.maps['countries/ir/ir-all'],
joinBy: 'hc-key',
states: {
hover: {
color: '#BADA55'
}
},
dataLabels: {
enabled: true,
style: {
textShadow: '',
},
format: '<span style="color:black">{point.name}</span>',
}
}, {
name: 'Report 2',
data: [{
'hc-key': "ir-wa",
value: '3000',
}, {
'hc-key': "ir-ea",
value: '3000',
}],
mapData: Highcharts.maps['countries/ir/ir-all'],
joinBy: 'hc-key',
states: {
hover: {
color: '#BADA55'
}
},
dataLabels: {
enabled: true,
style: {
textShadow: '',
},
format: '<span style="color:black">{point.name}</span>',
}
}]
});
});

TypeError: Highcharts[h] is not a function

My Plnkr link: http://plnkr.co/edit/S0BKjrgxz564oCPs9nCw?p=preview
Any idea why I would be getting this error? Not sure where Highcharts[h] is...
Controller Code
(function() {
angular.module('highChartTest', ['ui.bootstrap', 'highcharts-ng'])
.controller('MyHighChart', ['$scope', '$timeout', MyHighChart]);
// HighCharts Column Chart
function MyHighChart($scope, $timeout) {
var vs = $scope;
ticker = 'GOOG',
vs.chartObject = {};
var dayHolder = [];
var count = 0;
_.times(97, function() {
dayHolder.push({
'x': count++,
'y': count++
});
});
// Default data fed into navigator:
vs.navigatorData = dayHolder;
this.config = {
options: {
ignoreHiddenSeries: false,
credits: { enabled: true, text: 'www.tickertags.com' },
legend: {
itemStyle: {
color : "#333333",
cursor : "pointer",
fontSize : "10px",
fontWeight : "normal"
},
enabled : true,
floating : true,
align : 'left',
verticalAlign: 'top',
x: 60
},
chart : {
title : { text: '' },
subtitle : { text: '' },
renderTo : 'chart1',
zoomType : 'x',
events: {
load: function () {
// HighChart loaded callback:
broadcastChartLoaded();
console.log('config.chart.events.load...');
},
redraw: function(event) {
// console.log(' chart events redraw fired');
}
}
},
scrollbar: {
enabled : false,
liveRedraw : false
},
navigator : {
enabled: true,
adaptToUpdatedData: true,
// enabled: false,
// adaptToUpdatedData: false,
series : {
data : vs.navigatorData
}
},
rangeSelector: {
enabled: false,
},
exporting: { enabled: false }
},
exporting: { enabled: false },
useHighStocks: true,
xAxis : {
ordinal: false,
dateTimeLabelFormats : {
hour : '%I %p',
minute : '%I:%M %p'
},
events : {
// afterSetExtremes : afterSetExtremes,
// setExtremes : setExtremes
},
minRange: 3600 * 1000 // one hour
},
yAxis: [{ // Primary yAxis
labels: {
format: '${value:.2f}',
style: {
color: '#4C73FF',
}
},
title: {
text: 'Price',
style: {
color: '#4C73FF',
}
}
},
{ // Secondary yAxis
gridLineWidth: 0,
title: {
text: 'Mentions',
style: {
color: '#FDE18D'
// Pick one - "#FDE18D", "#7DD0FA", "#58A6EC"
}
},
opposite: false
}],
func: function(chart) {
vs.chartObject = chart;
},
series : [{
zIndex: 1000,
yAxis: 0,
showInLegend: true,
color: '#4C73FF',
data: dayHolder,
type: 'line',
name: 'SPY',
dataGrouping: {
enabled: true
}
}]
};
function broadcastChartLoaded() {
console.log('broadcastChartLoaded!!!');
}
$timeout(function() {
console.log('inside timeout, now add 1st series');
var quoteData = vs.navigatorData;
vs.chartObject.addSeries({
zIndex : 1000,
yAxis : 0,
name : ticker,
data : quoteData,
type : 'line',
color : '#4C73FF',
showInLegend : true,
dataGrouping : {
enabled: true
}
}, true);
}, 2000);
}
})();
You were using highcharts, and not the highstock.js library, but specifying highstock in the config.
http://plnkr.co/edit/mr4STsk3ekVOwXpZVgk0?p=preview
<script data-require="highstock#4.2.3" data-semver="4.2.3" src="//cdnjs.cloudflare.com/ajax/libs/highstock/4.2.3/highstock.js"></script>

Categories

Resources