Highcharts custom error handler - charts

We are using highcharts to plot multiple charts on a single HTML page.
However one/some of the chart throw highchart error and we like to capture those error and show different error to user.
For this highcharts do provide custom error handler. But this custom error handler does not provide information about specific chart throwing that error.
Here that JS Fiddle provided by highcharts, which works fine for a chart :
Highcharts.error = function (code, true) {
// See
https://github.com/highcharts/highcharts/blob/master/errors/errors.xml
// for error id's
Highcharts.charts[0].renderer
.text('Chart error ' + code)
.attr({
fill: 'red',
zIndex: 20
})
.add()
.align({
align: 'center',
verticalAlign: 'middle'
}, null, 'plotBox');
};
http://jsfiddle.net/gh/get/library/pure/highslide-software/highcharts.com/tree/master/samples/highcharts/chart/highcharts-error/
Any idea how can I use this custom error handler per chart?
I'm using new Highcharts.Charts(options) to create new chart, but don't see way to specify error handler per chart.
Additional info: Charts are refreshed/appended using data through APIs. User that configures chart also configures refresh interval and query to use for chart.

Error handling in HighCharts does not make much sense. It would make more sense to pass the chart instance to Highcharts.error (like Kamil Kulig wrote) or to have an error event in chart.events. Anyways
here is a solution I came up with:
Create an array of errors:
var chartErrors = [];
Create an error handler which will push errors into the chartErrors. Error objects I'm making look like this: {"chartIndex": <chart index>, "errorCode": <error code>}. All charts are added to the Highcharts.charts array when they are created so we can use Highcharts.charts.length - 1 for the chartIndex.
Highcharts.error = function (code) {
// See https://github.com/highcharts/highcharts/blob/master/errors/errors.xml
// for error id's
chartErrors.push({"chartIndex": Highcharts.charts.length - 1, "errorCode":code});
};
After initiating all charts we will have an array of errors. We can call forEach on this array and handle errors the way we want.
chartErrors.forEach(function(c) {
Highcharts.charts[c.chartIndex].renderer
.text('Chart error ' + c.errorCode)
.attr({
fill: 'red',
zIndex: 20
})
.add()
.align({
align: 'center',
verticalAlign: 'middle'
}, null, 'plotBox');
});
Working example:
Note: I've wrapped the code in a self invoking function to prevent leaking variables to global scope.
(function() {
var chartErrors = [];
Highcharts.error = function (code) {
// See https://github.com/highcharts/highcharts/blob/master/errors/errors.xml
// for error id's
chartErrors.push({"chartIndex": Highcharts.charts.length - 1, "errorCode":code});
};
Highcharts.chart('container1', {
title: {
text: 'Demo of Highcharts error handling'
},
xAxis: {
categories: ['Jan', 'Feb', 'Mar', 'Apr', 'May']
},
yAxis: {
type: 'logarithmic',
min: 0
},
series: [{
data: [1, 3, 2],
type: 'column'
}]
});
Highcharts.chart('container2', {
title: {
text: 'Solar Employment Growth by Sector, 2010-2016'
},
subtitle: {
text: 'Source: thesolarfoundation.com'
},
yAxis: {
title: {
text: 'Number of Employees'
}
},
legend: {
layout: 'vertical',
align: 'right',
verticalAlign: 'middle'
},
plotOptions: {
series: {
label: {
connectorAllowed: false
},
pointStart: 2010
}
},
series: [{
name: 'Installation',
data: [43934, 52503, 57177, 69658, 97031, 119931, 137133, 154175]
}, {
name: 'Manufacturing',
data: [24916, 24064, 29742, 29851, 32490, 30282, 38121, 40434]
}, {
name: 'Sales & Distribution',
data: [11744, 17722, 16005, 19771, 20185, 24377, 32147, 39387]
}, {
name: 'Project Development',
data: [null, null, 7988, 12169, 15112, 22452, 34400, 34227]
}, {
name: 'Other',
data: [12908, 5948, 8105, 11248, 8989, 11816, 18274, 18111]
}],
responsive: {
rules: [{
condition: {
maxWidth: 500
},
chartOptions: {
legend: {
layout: 'horizontal',
align: 'center',
verticalAlign: 'bottom'
}
}
}]
}
});
Highcharts.chart('container3', {
title: {
text: 'Demo of Highcharts error handling'
},
xAxis: {
categories: ['Jan', 'Feb', 'Mar', 'Apr', 'May']
},
yAxis: {
type: 'logarithmic',
min: 0
},
series: [{
data: [1, 3, 2],
type: 'column'
}]
});
chartErrors.forEach(function(e) {
Highcharts.charts[e.chartIndex].renderer
.text('Chart error ' + e.errorCode)
.attr({
fill: 'red',
zIndex: 20
})
.add()
.align({
align: 'center',
verticalAlign: 'middle'
}, null, 'plotBox');
});
})();
<script src="https://code.highcharts.com/highcharts.js"></script>
<div id="container1" style="height: 400px"></div>
<div id="container2" style="height: 400px"></div>
<div id="container3" style="height: 400px"></div>

Highcharts error function is not adjusted to have a chart context as an argument, because it can be executed in different contexts too.
For example: error number 16 occurs when Highcharts/Highstock is loaded second time in the same page. It has nothing to do with the chart, because it depends on script importing only.
The workaround I found requires some searching and and a little bit of coding.
Refer to this live demo: http://jsfiddle.net/kkulig/a8nun9aL/
I found the place in the code responsible for throwing the error 10 (the one you used in your example). I overwrote this function (see this doc page for more information about overwriting in Highcharts: https://www.highcharts.com/docs/extending-highcharts/extending-highcharts) and added a chart variable (from Highcharts.Axis.prototype.setTickInterval scope) as the third argument:
if (
axis.positiveValuesOnly &&
!secondPass &&
Math.min(axis.min, pick(axis.dataMin, axis.min)) <= 0
) { // #978
H.error(10, 1, chart); // Can't plot negative values on log axis // MODIFIED LINE
}
It should be done for all errors you want to custom handle.
Now it can be used in custom Highcharts.error function:
Highcharts.error = function(code, stop, chart) {
// See https://github.com/highcharts/highcharts/blob/master/errors/errors.xml
// for error id's
Highcharts.charts[0].renderer
.text('Chart error ' + code + " on chart titled: " + chart.title.textStr)
(...)
You can add your own property in chart constructor options and find it in chart.options object.

Related

Why is my page not rendering EnhancedGrid

Good day to all, while studying dojo, I ran into a problem that I do not draw an EnhancedGrid on my page. this error appears in the browser console:
dojo.js.uncompressed.js:1321 Uncaught TypeError: Cannot read property 'get' of null
at Object.getFeatures (ObjectStore.js.uncompressed.js:241)
at Object._setStore (DataGrid.js.uncompressed.js:14511)
at Object.advice (dojo.js.uncompressed.js:8428)
at Object.c [as _setStore] (dojo.js.uncompressed.js:8408)
at Object.postCreate (DataGrid.js.uncompressed.js:14351)
at Object.l (dojo.js.uncompressed.js:10753)
at Object.postCreate (EnhancedGrid.js.uncompressed.js:90)
at Object.create (DataGrid.js.uncompressed.js:4330)
at Object.postscript (DataGrid.js.uncompressed.js:4243)
at new <anonymous> (dojo.js.uncompressed.js:10950)
the grid drawing script looks like this:
var blogStore;
/**
* Creates Dojo Store.
*/
require(["dojo/store/JsonRest",
"dojo/data/ObjectStore"
], function (JsonRest, ObjectStore) {
blogJsonStore = new JsonRest({
handleAs: 'json',
target: 'http://localhost:8080/myservice'
});
var data = {
identifier: 'id',
items: []
};
blogJsonStore.query({
start: 0,
count: 10
}).then(function (results) {
var res =[];
res = results;
if (0 === res.length){
data.items.push("There are no entries in this blog. Create a post!!!")
}else {
data.items.push(results)
}
});
blogStore = new ObjectStore({data: data});
});
/**
* Creates Dojo EnhancedGrid.
*/
require(["dojox/grid/EnhancedGrid",
"dojox/grid/enhanced/plugins/Filter",
"dojox/grid/enhanced/plugins/NestedSorting",
"dojox/grid/enhanced/plugins/Pagination",
"dojo/domReady!"
], function (EnhancedGrid) {
Grid = new EnhancedGrid({
id: 'grid',
store: blogStore,
structure: [
{ name: 'Message', field: 'text', datatype: 'string',
width: 'auto', autoComplete: true }
],
rowsPerPage: 5,
rowSelector: "20px",
selectionMode: "single",
plugins: {
nestedSorting: true,
pagination: {
description: true,
pageStepper: true,
sizeSwitch: true,
pageSizes: ["5","10","15","All"],
maxPageStep: 4,
position: "bottom"
}
}
});
Grid.placeAt('resultDiv');
Grid.startup();
});
if you remove the blog "Creates Dojo Store." it renders normally
Help me solve the problem. Thank you in advance for any help

ApexCharts: Hide every nth label in chart

I would like to hide some of the labels from my chart made with ApexCharts.js. I am coming from Frappé Charts, which has a feature called "continuity." It allows you to hide labels if they do not comfortably fit, because the chart is a timeseries chart.
My ApexChart looks like this:
I would like to remove many of the dates, but still have them appear in the tooltip. I was able to do this in Frappé Charts and it looked like this:
Here's my code for the Apex chart:
var options = {
chart: {
animations: { enabled: false },
toolbar: { show: false },
zoom: { enabled: false },
type: 'line',
height: 400,
fontFamily: 'PT Sans'
},
stroke: {
width: 2
},
theme: {
monochrome: {
enabled: true,
color: '#800000',
shadeTo: 'light',
shadeIntensity: 0.65
}
},
series: [{
name: 'New Daily Cases',
data: [2,0,0,0,0,0,0,1,0,1,0,7,1,1,1,8,0,11,2,9,8,21,17,28,24,20,38,39,36,21,10,49,45,44,52,74,31,29,43,28,39,58,30,47,50,31,28,79,39,54,55,33,42,39,41,52,25,30,37,26,30,35,42,64,46,25,35,45,56,45,64,34,34,32,40,65,56,64,55,37,61,51,70,81,76,64,71,61,56,52,106,108,104,33,57,82,71,67,68,63,71,32,70,65,98,52,72,87,66,85,90,47,164,123,180,119,85,66,122,65,155,191,129,144,175,224,234,240,128,99,141,131,215,228,198,152,126,201,92,137,286,139,236,238,153,170,106,61]
}],
labels: ['February 28','February 29','March 1','March 2','March 3','March 4','March 5','March 6','March 7','March 8','March 9','March 10','March 11','March 12','March 13','March 14','March 15','March 16','March 17','March 18','March 19','March 20','March 21','March 22','March 23','March 24','March 25','March 26','March 27','March 28','March 29','March 30','March 31','April 1','April 2','April 3','April 4','April 5','April 6','April 7','April 8','April 9','April 10','April 11','April 12','April 13','April 14','April 15','April 16','April 17','April 18','April 19','April 20','April 21','April 22','April 23','April 24','April 25','April 26','April 27','April 28','April 29','April 30','May 1','May 2','May 3','May 4','May 5','May 6','May 7','May 8','May 9','May 10','May 11','May 12','May 13','May 14','May 15','May 16','May 17','May 18','May 19','May 20','May 21','May 22','May 23','May 24','May 25','May 26','May 27','May 28','May 29','May 30','May 31','June 1','June 2','June 3','June 4','June 5','June 6','June 7','June 8','June 9','June 10','June 11','June 12','June 13','June 14','June 15','June 16','June 17','June 18','June 19','June 20','June 21','June 22','June 23','June 24','June 25','June 26','June 27','June 28','June 29','June 30','July 1','July 2','July 3','July 4','July 5','July 6','July 7','July 8','July 9','July 10','July 11','July 12','July 13','July 14','July 15','July 16','July 17','July 18','July 19','July 20','July 21','July 22','July 23','July 24'],
xaxis: {
tooltip: { enabled: false }
},
}
var chart = new ApexCharts(document.querySelector("#chart"), options);
chart.render();
<script src="https://cdn.jsdelivr.net/npm/apexcharts"></script>
<div id="chart"></div>
And here's my code for the Frappé Chart if it helps:
const data = {
labels: ['February 28','February 29','March 1','March 2','March 3','March 4','March 5','March 6','March 7','March 8','March 9','March 10','March 11','March 12','March 13','March 14','March 15','March 16','March 17','March 18','March 19','March 20','March 21','March 22','March 23','March 24','March 25','March 26','March 27','March 28','March 29','March 30','March 31','April 1','April 2','April 3','April 4','April 5','April 6','April 7','April 8','April 9','April 10','April 11','April 12','April 13','April 14','April 15','April 16','April 17','April 18','April 19','April 20','April 21','April 22','April 23','April 24','April 25','April 26','April 27','April 28','April 29','April 30','May 1','May 2','May 3','May 4','May 5','May 6','May 7','May 8','May 9','May 10','May 11','May 12','May 13','May 14','May 15','May 16','May 17','May 18','May 19','May 20','May 21','May 22','May 23','May 24','May 25','May 26','May 27','May 28','May 29','May 30','May 31','June 1','June 2','June 3','June 4','June 5','June 6','June 7','June 8','June 9','June 10','June 11','June 12','June 13','June 14','June 15','June 16','June 17','June 18','June 19','June 20','June 21','June 22','June 23','June 24','June 25','June 26','June 27','June 28','June 29','June 30','July 1','July 2','July 3','July 4','July 5','July 6','July 7','July 8','July 9','July 10','July 11','July 12','July 13','July 14','July 15','July 16','July 17','July 18','July 19','July 20','July 21','July 22','July 23','July 24'],
datasets: [{
name: 'Cumulative Cases',
values: [2,0,0,0,0,0,0,1,0,1,0,7,1,1,1,8,0,11,2,9,8,21,17,28,24,20,38,39,36,21,10,49,45,44,52,74,31,29,43,28,39,58,30,47,50,31,28,79,39,54,55,33,42,39,41,52,25,30,37,26,30,35,42,64,46,25,35,45,56,45,64,34,34,32,40,65,56,64,55,37,61,51,70,81,76,64,71,61,56,52,106,108,104,33,57,82,71,67,68,63,71,32,70,65,98,52,72,87,66,85,90,47,164,123,180,119,85,66,122,65,155,191,129,144,175,224,234,240,128,99,141,131,215,228,198,152,126,201,92,137,286,139,236,238,153,170,106,61],
chartType: 'line'
}]
}
const chart = new frappe.Chart('#chart', {
data: data,
type: 'line',
height: 250,
animate: false,
barOptions: {
spaceRatio: 0.25
},
colors: ['#800000'],
tooltipOptions: {
formatTooltipY: d => d.toLocaleString()
},
axisOptions: {
xAxisMode: 'tick',
xIsSeries: true
},
lineOptions: {
hideDots: true,
regionFill: true
}
})
<script src="https://cdn.jsdelivr.net/npm/frappe-charts#1.5.2/dist/frappe-charts.min.iife.min.js"></script>
<div id="chart"></div>
I've tried using the formatter callback function to return only every 10th value, but things get all out of position and the tooltips don't work. I get similar problems returning an empty string or a space for the values I wish to exclude (but still include in the tooltip).
What I do is calculate the ratio between the area's width and the number of ticks, and if that ratio is above a certain number, I add a classname to the chart or it's wrapper and there I write:
.apexcharts-xaxis-label{
display: none;
&:nth-child(5n){ display:revert; }
}
So every 5th label is shown and the rest are hidden.
You can also set up a resizeObserver to add/remove the special class.
This require the below config to be given to the chart:
xaxis: {
labels: {
rotate: 0, // no need to rotate since hiding labels gives plenty of room
hideOverlappingLabels: false // all labels must be rendered
}
}
You can try 2 things.
xaxis: {
type: 'datetime',
}
You can convert the x-axis to datetime and labels will align as shown below
Or
You can stop rotation of the x-axis labels using
xaxis: {
labels: {
rotate: 0
}
}
which produces the following result.
Vsync answer have not worked for me. It needed a little modification:
.apexcharts-xaxis-texts-g text[id^='SvgjsText'] {
display: none;
}
.apexcharts-xaxis-texts-g text[id^='SvgjsText']:nth-of-type(5n) {
display: revert;
}
labels: ['',this.props.itemNames], //"(labels: [the label , the label below])"

ExtJS MultiSelect Edit - Not working for multi value selection

I have a GridEditPanel where the 1st column is a combobox with multiSelect. The values are being loaded correctly from the DB and is being written in the DB correctly as well. In the event where the the combobox has a single value, the drop-down highlights the value correctly as well.
The issue is when the combobox has multiple values, it displays the values correctly, however during edit the multiple values are not selected.
Model:
extend: 'Ext.data.Model',
idProperty: 'contactTypeID',
fields: [
{
name: 'contactTypeID',
type: 'string'
},
{
name: 'contactType',
type: 'string'
}
],
View GridEditPanel
emptyText: "There are no contacts.",
insertErrorText: 'Please finish editing the current contact before inserting a new record',
addButtonText: 'Add Contact',
itemId: 'contacts',
viewConfig: {
deferEmptyText: false
},
minHeight: 130,
initComponent: function () {
var me = this,
contactTypes;
// Creating store to be referenced by column renderer
contactTypes = Ext.create('Ext.data.Store', {
model: '********',
autoLoad: true,
listeners: {
load: function () {
me.getView().refresh();
}
}
});
this.columns = [
{
text: 'Contact Role',
dataIndex: 'contactRoleID',
flex: 1,
renderer: function (value) {
// Lookup contact type to get display value
//If a contact has multiple roles, use split by ',' to find display values.
if (value.includes(',')) {
var a = value.split(','), i, contTypeIds = [];
var contTypes = new Array();
for (i = 0; i < a.length; i++) {
contTypeIds.push(a[i]);
contTypes.push(contactTypes.findRecord('contactTypeID', a[i], 0, false, false, true).get('contactType'));
}
console.log('Multi Render Return Value: ' + contTypes);
return contTypes;
}
else {//if not a contact will only have one role.
var rec = contactTypes.findRecord('contactTypeID', value, 0, false, false, true); // exact match
console.log('Single Render Return Value: ' + rec.get('contactType'));
return rec ? rec.get('contactType') : '<span class="colselecttext">Required</span>';
}
},
align: 'center',
autoSizeColumn: true,
editor: {
xtype: 'combobox',
store: contactTypes,
multiSelect: true,
delimiter: ',',
forceSelection: true,
queryMode: 'local',
displayField: 'contactType',
valueField: 'contactTypeID',
allowBlank: false
}
},
I cannot see the model of GridEditPanel, but I assume you are using the wrong field type, string instead of array (Have a look at the converter function, maybe it will help you to fix the problem). I wrote a small post in my blog about multiSelect combobox editor in editable grid. The sample works with v4.2
Hope it will help you to fix the bug.

Google charts multiline dynamic charts

Am new to PHP coding & have a few google charts working. All of these charts I've generated so far are based on (date,number of event occurrences) type of chart. I'm trying to plot a google chart whose data is the output of SQL query.
The output of SQL query looks as below
|SERIES|DATE_1|DATE_2|DATE_3|
|a|2|3|
|b|4|6|
|c|7|8|
Both SERIES & DATE_1 can vary. That is to say, based on various conditions in the SQL query, the number of DATE_ can be vary & so can the SERIES.
I would then have to pass this output to the google chart plot code.
Here is what i've tried coding so far
$link = mysql_connect("localhost", "user", "pass");
$dbcheck = mysql_select_db("database");
if ($dbcheck) {
$chart_array_1[] = "['MY_DATE','MY_NAME','#NUM_OCCURENCES']";
$result = mysql_query($sql);
if (mysql_num_rows($result) > 0) {
while ($row = mysql_fetch_assoc($result)) {
$my_date=$row["MY_DATE"];
$my_ins=$row["MY_NAME"];
$my_count=$row["MY_COUNT"];
$chart_array_1[]="['".$my_date."','".$my_ins."',".$my_count."]";
}
}
}
mysqli_close($link);
<script type="text/javascript">
// Load the Visualization API and the piechart package.
google.load('visualization', '1', {'packages':['corechart']});
// Set a callback to run when the Google Visualization API is loaded.
google.setOnLoadCallback(drawChart);
function drawChart() {
// Create our data table out of JSON data loaded from server.
var data_1 = google.visualization.arrayToDataTable([<?php echo (implode(",", $chart_array_1)); ?>])
var options = {
bar: {groupWidth: "6%"},
trendlines: {
1: {
type: 'linear',
color: 'green',
lineWidth: 3,
opacity: 0.3,
showR2: true,
visibleInLegend: true
}
},
chartArea: {
left: 70,
top: 61,
width:'95%',
height:'70%'
},
curveType: 'function',
//width: 1600,
height: 400,
pointSize: 4,
lineWidth: 2,
visibleInLegend: false,
vAxis: {
//title: "GC#",
logScale: true,
titleTextStyle: {
color: 'black'
}
},
hAxis: {
title: "TIMELINE",
titleTextStyle: {
bold: false,
color: 'black'
}
},
legend: {
position: 'top',
alignment: 'center',
textStyle: {
color: 'blue'
}
}
};
var chart_1 = new google.visualization.LineChart(document.getElementById('plot1'));
chart_1.draw(data_1, options);
}
</script>
I'm unable to plot the graph. I get the error "Data column(s) for axis #0 cannot be of type string×". Could someone please help me here.
I'd like to see a,b,c etc as separate series each while the date goes on to the X-Axis. Please note am after generating data dynamically using SQL query & not a static array which most examples demonstrate. Could someone please help?
Managed to implement thing a different way. Hence this question can be ignored.

Charts in ExtJS3

I'm using ExtJS3 and i want to put this chart into a panel with a dynamic store
http://dev.sencha.com/deploy/ext-3.4.0/examples/chart/pie-chart.html
I tried to include this chart into my panel code but it didn't work.
Does anybody has a solution or an example for a chart included into a panel in ExtJS3
Thank you
I used your example to generate the chart using a dynamic store:
Ext.chart.Chart.CHART_URL = 'http://dev.sencha.com/deploy/ext-3.4.0/resources/charts.swf';
Ext.onReady(function(){
var store = new Ext.data.JsonStore({
url: "sample_data.php",
root: 'results',
fields: [
{name: 'season'},
{name: 'total'}
]
});
new Ext.Panel({
width: 400,
height: 400,
title: 'Pie Chart with Legend - Favorite Season',
renderTo: 'container',
items: {
store: store,
xtype: 'piechart',
dataField: 'total',
categoryField: 'season',
//extra styles get applied to the chart defaults
extraStyle:
{
legend:
{
display: 'bottom',
padding: 5,
font:
{
family: 'Tahoma',
size: 13
}
}
}
}
});
});
where http://dev.sencha.com/deploy/ext-3.4.0/resources/charts.swf is the target where you can find the chart and sample_data.php returns the following json:
{"results":[
{"total":"150","season":"Summer"},
{"total":"245","season":"Fall"},
{"total":"117","season":"Winter"},
{"total":"184","season":"spring"}
]}
Note: This should normally be set to a local resource.
Hope this helps.