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.
Related
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.
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.
I have several morris.js charts that populate from my databases depending on certain search terms. Im using the following code to build a "Legend" for my donut charts. The code works fine but Im struggling with adding both a number and text, I'm getting a console error:
ReferenceError: value not defined
Here is the code I'm currently using (works great):
// Build the Donut:
var donut = Morris.Donut({
element: 'donut',
data : donutParts,
colors: color_array
});
// Build the Legend:
donut.options.data.forEach(function(label, i){
var legendItem = $('<span></span>').text(label['label']).prepend('<i> </i>');
legendItem.find('i').css('backgroundColor', donut.options.colors[i]);
$('#legend').append(legendItem)
})
This will give me a legend with matching color squares with the appropriate labels eg:
[red] UK
But I want:
[red] # UK
I've tried using .integer and other variations like so:
// Build the Donut:
var donut = Morris.Donut({
element: 'donut',
data : donutParts,
colors: color_array
});
// Build the Legend:
donut.options.data.forEach(function(label, i){
var legendItem = $('<span></span>').text(label['label']).integer(['value']).prepend('<i> </i>');
legendItem.find('i').css('backgroundColor', donut.options.colors[i]);
$('#legend').append(legendItem)
})
But this gives me the error that value is not defined, i want to take the value(v) from donutParts
Here is my donutParts variable:
// Fetch the data to populate the donut chart:
var chartData = JSON.parse( $('#chartData').val() );
// Break up the object into parts of the donut:
var donutParts = [];
$.each( chartData, function(k,v){
donutParts.push({
label: k,
value: v
});
});
Any help? cheers!
ANSWER
The following code produces the desired output:
// Build the Legend:
donut.options.data.forEach(function(label, i){
var legendItem = $('<span></span>').text(label['value']+" "+label['label']).prepend('<i> </i>');
legendItem.find('i').css('backgroundColor', donut.options.colors[i]);
$('#legend').append(legendItem)
})
This is a SS of the legend output after implementing the given answer
Big thank you to #WillParky93
So what I said in the comments was technically wrong but after further reading this is what the 'donut.options.data.forEach' is doing.
Create an object dom of <span></span> -> add the text from label['label'] (which appears to be UK in your example) -> add some <i> tags.
THEN
Find the newly created tags -> add the CSS rule for background colour
THEN
add it to your #legend
So the solution was actually more simple when thinking of it like this; the answer be just this:
// Build the Legend:
donut.options.data.forEach(function(label, i){
var legendItem = $('<span></span>').text(label['value']+" "+label['label']).prepend('<i> </i>');
legendItem.find('i').css('backgroundColor', donut.options.colors[i]);
$('#legend').append(legendItem)
})
The change is in .text(label['label']) which is now .text(label['value']+" "+label['label'])
I'm using the following json to generate my PieChart, but it generates a blank chart with no error.
The following block is my code to generate the chart:
$.post('/admin/relatorios/getVendasCidadeChart', {
dt_inicio: $("#datepicker-from").val(),
dt_fim: $("#datepicker-to").val()
}).done(function(donutData){
var dados = new google.visualization.DataTable(donutData);
var options = {
title: "Vendas por Cidade",
width: "100%",
height: "100%",
};
var chart = new google.visualization.PieChart(document.getElementById('vendas-cidade'));
chart.draw(dados, options);
});
The json output by the post request:
{"cols":[{"id":"","label":"Loja","type":"string"},{"id":"","label":"Valor(R$)","type":"number"}],"rows":[{"c":[{"v":"Loja Shopping"},{"v":"8620.00"}]},{"c":[{"v":"Loja Centro"},{"v":"10240.00"}]}]}
Chart generated:
http://i.stack.imgur.com/Dfhe4.png
Update 1
Fiddle reproducing the problem:
https://jsfiddle.net/thatmesg/1/
I had similar "issue" but that wasn't an issue. All my pie slices were just returning 0 which may happen and it's logically correct.
Add sliceVisibilityThreshold: 0 to your options variable. It should display the legend even if there will be no data. If you'll see the legend then we can assume that Google Chart library works as expected. If not... then we'll investigate further.
var options = {
legend: { position: 'top', maxLines: 6 },
sliceVisibilityThreshold: 0
};
EDIT:
I see that you're using wrong type of brackets:
That's how your JSON should look like. If you generate it using PHP, don't forget to use json_encode.
var data = google.visualization.arrayToDataTable([["Category","Hours"],["Compliance \/ Policy ($0.00)",0],["Harrassment ($0.00)",0],["Productivity ($0.00)",0],["Skills Gap ($0.00)",0],["Values Behaviour ($0.00)",0]]);
Also I see that yous JSON contains "keys". You should do something like (remove keys and pass just values to the JSON):
$final['json'] = json_encode(array_values($pie_hours));
The issue was with my values... The column "Valor" is defined as number, but the values was coming as strings with quotes...
Just removed the quotes from my JSON and it worked
{"cols":[{"id":"","label":"Loja","pattern":"","type":"string"},{"id":"","label":"Valor(R$)","pattern":"","type":"number"}],"rows":[{"c":[{"v":"Loja Shopping","f":null},{"v":8620.00,"f":null}]},{"c":[{"v":"Loja Centro","f":null},{"v":10240.00,"f":null}]}]}
Updated fiddle
Though I have successfully colored the bars of google chart individually but not able to keep them when we hover mouse over it. It is getting reset back to blue(which is default).
Here is the jsfiddle of what I have done jsfiddle.
I tried to control the hover behaviour with multiple ways like below.
This I am keeping outside (document.ready) but inside script tag.
1)
$('#chart_div').hover(
function() {
$('#chart_client').hide(); // chart_client is another google chart div.
}, function() { // just for testing I was doing hide/show of that.
$('#chart_client').show();
}
);
2)
$("#chart_div").on({
mouseenter: function () {
$('#chart_client').hide();
},
mouseleave:function () {
$('#chart_client').show();
}
},'rect');
3)
google.visualization.events.addListener('#chart_div', 'ready', function () {
$('#chart_div rect').mouseover(function (e) {
alert('hello');
});
});
I must be doing something wrong, could you please tell me what and where.
I solved it using below code. Earlier I was trying to create charts using dynamically adding rows into chart(please visit my jsfiddle) but with this below approach I am first preparing data(converting dynamic to static) and adding that static data in to chart's 'arrayToDataTable' method.
google.load("visualization", "1", {packages:["corechart"]});
google.setOnLoadCallback(drawUserKeywordChart);
function drawUserKeywordChart() {
var val = 'Tax:47;Finance:95;Awards:126;Bank:137;Debt:145;';
var length = val.length;
var array = [];
//preparing data
while(length>0){
var sepAt = val.indexOf(";");
var value = parseInt(val.substring(val.indexOf(":")+1, sepAt));
array.push(val.substring(0, val.indexOf(":")));
array.push(value);
val = val.substring(val.indexOf(";")+1, length);
length = val.length;
}
var data = google.visualization.arrayToDataTable([
['Keyword', 'Occurences', { role: 'style' }],
[array[0], array[1], '#8AA3B3'],
[array[2], array[3], '#A9B089'],
[array[4], array[5], '#848C49'],
[array[6], array[7], '#44464A'],
[array[8], array[9], '#704610'],
]);
var options = {
title: 'Keyword Matches',
width: 660,
height: 450,
titleTextStyle:{bold:true,fontSize:20},
legend:{position:'none'}
};
var chart = new google.visualization.ColumnChart(document.getElementById('chart_keyword1'));
chart.draw(data, options);
}
Please advice if you find anything wrong here or you have better approach than this.