Renaming flot pie chart ID - javascript

I'm trying to create a pie chart through the use of flot charts. I have successfully managed to create one with the following code:
HTML:
<div class="row">
<div class="col-md-6">
<div id="pie-chart"></div>
</div>
</div>
Javascript:
var data = [];
data[0] = { label: "Vertification successful", data: 9 };
data[1] = { label: "Vertification failed", data: 2 };
var series = Math.floor(Math.random() * 10) + 1;
$.plot($("#pie-chart"), data,
{
series: {
pie: {
show: true,
}
},
grid: { hoverable: true },
});
And it displays just fine.
The thing is, if I change the ID of the div element to "pie-chart1" (rather than "pie-chart")
and update the javascript accordingly:
$.plot($("#pie-chart1"), data,
I get the following error:
Uncaught Invalid dimensions for plot, width = 501, height = 0
What on earth could be causing this? I simply wanna rename the ID which apparently for some reason is impossible.

It's very likely that there is some CSS or possibly JS elsewhere on your site that expects the div to be called pie-chart. You need to ensure that it still applies to the new div. For example, if you had:
#pie-chart {
width: 400px;
height: 300px;
}
When you change the ID of the div, you need to update that reference too, or else the placeholder's height and width become undefined, which Flot cannot handle.
If your goal in adding that number is to create several charts, then you should use a class to apply the styles rather than an ID.

Related

Instead of creating new chart in ChartJS, the new updated chart keeps the old data and adds the new

I have been trying to solve this problem with ChartJS for a few days now, and I am completely stumped
My program shows the user a set of input elements they use to select data needing to be charted, plus a button that has an event to chart their data. The first chart works great. If they make a change to the data and click the button a second, third, or more time, all the data from the previous charts is plotted, PLUS their most recent selection.
It is behaving exactly like you might expect if the chart.destroy() object is not working, or perhaps would work if I created the chart object using a CONST (and could therefore add new data but not delete the beginning data).
I have tried all combinations of the browsers, chartjs and jquery libraries below:
Three different browsers:
• Chrome: Version 107.0.5304.121 (Official Build) (64-bit)
• Microsoft Edge: Version 107.0.1418.56 (Official build) (64-bit)
• Firefox: 107.0 64-bit
I have tried at least three different versions of Chart.js, including
• Versions 3.9.1
• 3.6.2
• 3.7.0
Jquery.js
• v3.6.1
• v1.11.1
Other things I have tried:
"use strict" (no luck)
In addition to destroying the chart object, removed the div containing the canvas, and appending it again.
using setTimeout() function before updating the chart after destroying it (because I thought maybe giving the destroy method more time might help)
type here
Software:
<script type="text/javascript" src="js/jquery.js"></script>
<script type="text/javascript" src="js/chart.js"></script>
<script type="text/javascript" src="js/dropdownLists.js"></script>
<script type="text/javascript" src="js/chartDataFunctions.js"></script>
<script type="text/javascript" src="js/chartJSFunctions.js"></script>
<body>
<div class = metadatasetup4" id = "buttons">
<button class="download" id="getchart" value="Get Chart">Chart</button>
<button class="download" id="downloadchart" value="Download">Download</button>
</div>
<div id = "bigchartdiv" class="bigchart">
<canvas id="myChart"></canvas>
</div>
</body>
<script>
$(window).on('load',function(){
//NOTE 1: In of my attempts to troubleshoot I tried strict mode (it didn't work)
//"use strict";
let data = {
labels: lbl,
datasets: [
]
};
let config = {
type: 'line',
data: data,
options: {
scales: {
y: {
type: 'linear',
display: true,
position: 'left',
min:0,
pointStyle:'circle',
},
y1: {
type: 'linear',
display: true,
position: 'right',
suggestedMax: 25,
min: 0,
pointStyle: 'cross',
// grid line settings
grid: {
drawOnChartArea: false, // only want the grid lines for one axis to show up
},
},
}
}
};
// NOTE 2: The next line below, beginning with "var bigChartHTML =" was one of my later attempts to
// solve the problem. It didn't work, but my thought process was that if I removed
// the div containing the canvas, AND destroyed the chart object, that appending a "fresh"
// chart div to the body might be a work-around. This did not work.
var bigChartHTML = '<div id = "bigchartdiv" class="bigchart"><canvas id="myChart"></canvas></div>'
let ctx = document.getElementById('myChart').getContext('2d');
let bigChart = null;
// The getChartData() function below uses Ajax to populate various dropdown lists
// which enable the user to select the data is to be charted.
// There are no chartjs-related operations in getChartData()
getChartData();
$('#buttons').on('click','#getchart',function(){
if (bigChart!=null) {
//removeData(bigChart);
bigChart.destroy();
//bigChart = 1;
}
$("#bigchartdiv").empty(); //for this and next 2 lines, see NOTE 2 above
$("#bigchartdiv").remove();
$(bigChartHTML).insertAfter("#chartcontrols");
bigChart = new Chart(document.getElementById('myChart'),config);
//NOTE 3: I thought maybe bigChart.destroy() took time, so I tried
// using the setTimeout function to delay updating the chart
// (didn't work, but I left it in the code, anyway.)
setTimeout(function() {updateChart(bigChart)}, 2000);
//updateChart(bigChart);
});
// NOTE: The updateChart() function is actually included in "js/chartDataFunctions.js"
function updateChart(chart) {
/*
This section of the program reads the HTML elements then uses them
to make an Ajax request to sql server, and these become the
parameters for the newDataSet() function below.
*/
newDataset(chart,firstElement,newdataset,backgroundcolor,color);
}
// NOTE: The newDataSet() function is actually included in "js/chartJSFunctions.js"
// I show it here for brevity.
// It decides which axis (y or y1) to use to plot the datasets
// the dataset is pushed into the data, and chart.update() puts it in the chart object
function newDataset(chart,label,data,bgcolor='white',color='rgb(255,255,255)') {
var maxValue = Math.max(...data);
if (Number.isNaN(maxValue)) {
return;
}
if (maxValue == 0) {
return;
}
var axisID = 'y';
var ptStyle = 'circle';
//var pStyle = 'circle';
if (maxValue < 50) {
axisID = 'y1';
bgcolor = 'white';
//ptStyle = 'Star'
}
chart.data.datasets.push({
label:label,
yAxisID:axisID,
data:data,
borderColor:color,
backgroundColor:bgcolor,
//pointStyle:ptStyle
});
chart.update();
}
});
</script>
I found a work-around that solves my problem, but I still think this is a bug in ChartJS. Before calling bigChart.destroy(), I now do two things: First, reset the data object back to it's original value, and second, reset the config object back to it's original value, THEN call bigChart.destroy().
I think the destroy() method should handle that for me, but in my case, for whatever reason, it doesn't.
So, what I have is a work-around, not really a solution, but I'll take it.

How to hide Fields and Strike-through Legends when the data is empty or Zero in Pie/Polar/Doughnut Chart?

Following is my resultant chart
Here the value of legends Happy and Very Happy is 0, hence it is overlapping each other and unable to read. So, How can I hide these values and strike through the legends while loading itself like in the below image? And yes, it is a dynamically loaded chart.
Link - Reference Pie Chart
Thanks in advance.
I am posting this answer hoping that, it will be helpful for someone later. You can also post a better solution if found.
After some deep diving into the library the files, I realised that is there are no direct answers to my question. But we can achieve that by emptying the label text in case of 0 data values.
For that, we must edit the chart options as follows,
public pieChartOptions: ChartOptions = {
responsive: true,
legend: {
position: 'top',
},
plugins: {
datalabels: {
formatter: (value, ctx) => {
const label = ctx.chart.data.labels[ctx.dataIndex];
if (ctx.dataset.data[ctx.dataIndex] > 0)
return label + " : " + ctx.dataset.data[ctx.dataIndex];
else
return "" // retun empty if the data for label is empty
},
}
},
showLines: true,
spanGaps: true,
cutoutPercentage: 1,
rotation: 15, // rotate the chart
};
Here in the function returns empty value in case the data for the corresponding label is 0. Also I rotate the chart 15deg to make the labels horizontal align in most cases.
Reference - Chart.js documentation
Hence I achieved a better view to the user and the overlapping issues are resolved. Thanks.

CanvasJS chart not rendering properly with slideshow/carousel container

I'm trying to make a Slideshow/carousel with pie charts in it. The charts load when the page loads, but only one is displayed at a time.
The problem is that when I move the slideshow to the side to display the second chart, instead of rendering within it's container it takes up the whole space of it's grandparent. Specifically, the first chart that renders is the correct size, any other chart is wrong.
This doesn't use any PHP or anything of the sort, just javascript, html, css and the canvasjs framework. I've tried forcing the graphs to be a fixed size but when I load the next one it just ignores all of the rules I set.
Here is the code:
/*--------- function that creates the charts ----------*/
function criaGrafico(id, valores, tp) {
setInner(id, '');
if (valores == '') return;
var dados = new Array;
var title = {
text: '',
};
var legenda = {
fontSize: "14",
verticalAlign: "bottom",
horizontalAlign: "left",
fontFamily: "sans-serif",
itemWrap: false
};
dados[0] = {
dataPoints: new Array,
indexLabelFontSize: 10,
indexLabelFontFamily: "sans-serif",
indexLabelFontColor: "darkgrey",
indexLabelLineColor: "darkgrey",
indexLabelPlacement: "outside",
indexLabelMaxWidth: 60,
type: tp,
showInLegend: false, // true mmm
legendMarkerType: 'square'
};
var opc = {
title: title,
legend: legenda,
data: dados
};
var chart = new CanvasJS.Chart(id, opc);
var campo = '';
for (var i = 0, lt = valores.length; i < lt; i++) {
campo = valores[i].split("|");
chart.options.data[0].dataPoints.push({
y: Decimal(campo[0]),
legendText: campo[4],
indexLabel: campo[2],
toolTipContent: campo[1],
color: campo[3]
/*,click:function(e){clicouGrafico(e)}*/
,
cursor: "pointer"
});
}
chart.render();
}
/* function that sets the parameters for the chart */
function graficoProj1() {
var val = new Array();
val[0] = '71|Finalizadas: 87 (71%)|Fin|green|Finalizadas';
val[1] = '9|Direcionadas: 12 (9%)|Dir|orange|Direcionadas';
val[2] = '18|Iniciadas: 22 (18%)|Ini|blue|Iniciadas';
criaGrafico('chart_inov3', val, "pie");
}
#chart_inov3 {
margin-left: 15px;
z-index: 0;
position: relative;
}
<div class="slideshow-container">
<div class="projSlides fade">
<div class='col-d-4 col-t-4'>
<div id='chart_container' style="height: 170px; width: 170px;">
<div id="chart_inov3"></div>
</div>
</div>
<!-- some other info that would stay besides the chart,
in the same parent container -->
</div>
</div>
Then, the function graficoProj1 is called when the window loads, along with the other charts that will go into the slideshow, like graficoProj2 and graficoProj3.
The result should be a chart that stays inside it's own container, like so:
However, when I press the "Next" button to the right to display the next slide, the other chart renders like this:
EDIT:
I suspect this might be linked to the way I display the containers of the slideshow, since they are first set as display: none and then they get to be display: block when focused. This might cause the charts to not render properly within their container since they are not shown. Rerendering them might fix this problem, but I'm still at a loss at how to.
Does anyone have any idea of what could be causing this?
I've fixed it already, forgot to update. The problem was in the "showSlide" javascript function (which was not in this post). When hidden, the charts don't render, so I just have to call the render function after display: block is shown.

Add custom parameter to info.contentsFunction

I need to be able to add some custom info to the pie.info.contentsFunction in Zoomcharts. I have multiple charts on the page, each one created like so...
var pc = new PieChart({
pie: {
innerRadius: 0.5,
},
container: chartContainer1,
area: { height: 500 },
data:chartData,
toolbar: {
"fullscreen": true,
"enabled": true
},
info: {
contentsFunction: boomChartTT
}
});
In the "boomChartTT" function I need to know what chart is being hovered upon. I'd like to be able to do something like this...
info: {
contentsFunction: boomChartTT(i)
}
...where 'i' is the index of the chart.
The reason I need to know the chart index is because I have some other data saved in an indexed array for each chart. The index of the chart matches the index of the data.
EXAMPLE: if user hovers on a slice in chart2 I'd want to pass '2' to the boomChartTT function so I can access the totals data for that chart (say, totalsData[2]).
I've done this in the past with other chart libraries by simply adding a data attribute to the chart container to give me the index like so...
<div id="chartContainer1" data-index="1"></div>
...and then I'm able to access the chartContainer from the hover function (contentsFunction) and then get that index.
I don't want to add the totals data to the actual chart data because I'd have to add it to each slice which is redundant.
Is there a way to do this?
Please let me know if my post is unclear.
EDITED TO ADD:
I don't think it matters but here is the boomChartTT function:
function boomChartTT(data,slice){
var tt="<div class=\"charttooltip\">";
if(data.name==="Others" || data.name==="Previous"){return tt+=data.name+"</div>";}
//var thisData=dataSearch(totalsData[i],"REFERRINGSITE",data.id);
tt+="<h5 class=\"strong\">"+data.id+"</h5>"+oHoverTable.render(thisData)+"</div>";
return tt;
}
The commented line is where I would need the index (i) to to get the correct totalsData.
SOLVED. I simply added "chartIndex" to the data like so...
for(var i=0;i<r.length;i++){
var thisDataObj ={
id:r[i].REFERRINGSITE,
value:r[i].PCTOFSALES,
name:r[i].REFERRINGSITE,
chartIndex: arguments[1],//<----- arguments[1] is the chart index
style: { expandable: false, fillColor: dataSearch(dataRSList,"REFERRINGSITE",r[i].REFERRINGSITE)[0].COLOR }
};
chartData.preloaded.subvalues.push(thisDataObj);
}
Then in the boomChartTT function...
function boomChartTT(data,slice){
var tt="<div class=\"charttooltip\">";
if(data.name==="Others" || data.name==="Previous"){return tt+=data.name+"</div>";}
var thisData=dataSearch(totalsData[data.chartIndex-1],"REFERRINGSITE",data.id);
tt+="<h5 class=\"strong\">"+data.id+"</h5>"+oHoverTable.render(thisData)+"</div>";
return tt;
}
I feared that adding custom fields to the chart data would break the chart (which I believe I've experienced with other libraries). So, there you go.

how to use angular-gridster and highcharts-ng directives together in angularjs

I am using angularjs-gridster (https://github.com/ManifestWebDesign/angular-gridster) with higharts-ng directive (https://github.com/pablojim/highcharts-ng/blob/master/README.md)
I am trying to generate these highcharts inside the grid cells. My problem is that the highcharts are occupying their default width and height (600px * 400px) even when i place my graph drawer function in a $timeout service. Here's the code:
HTML:
<div class="graph-list" gridster="gridsterOpts">
<ul>
<li gridster-item="graph.grid" class="graph-set" ng-repeat="graph in graphs | orderBy: 'number'">
<highchart id="{{'graph' + graph.number}}" class="graph" config="graph.config"></highchart>
</li>
</ul>
</div>
JS:
// inside the graph-list div controller
$scope.gridsterOpts = {
colums: 4,
rowHeight: 240,
margins: [10,10],
outerMargin: false,
draggable: {
enabled: false // whether dragging items is supported
}
};
$scope.graphs = {}; //
$scope.somefunction(){ /* this function populates the graphs object */ };
function drawGraphs(){ /* this function populates the graph.config object by looping through all the graph objects */ }
$timeout(function(){
drawGraphs();
});
I have tried creating watch on the grid-cell width and height but it shows no change. I have not given the highchart width and height explicitly in the graph.config options because I read in the highcharts-ng documentation that it takes the parent width and height by default but its not happening. Can anyone guide me what could be the possible problem.
Seems to me that the angularjs-gridster plugin is not able to set the grid width and height before the highcharts directive is able to render itself. Please help.
I eventually did it. I needed to add the chart.reflow() method (which just resizes the chart instead of redrawing it so better performance wise also, I guess) in the func() options as provided in the highcharts-ng documentation.
graph.config = {
options: { },
series: [],
func: function (chart) {
$timeout(function(){
chart.reflow();
})
}
}
Hope it helps someone else.

Categories