I'm trying to figure out chartjs' destroy method and it's not working quite right. I'm doing this on dummy data with a dummy button, but here's what I have.
Really simple html markup for testing:
<div class="container mt-5">
<div class="row">
<canvas id="myChart"></canvas>
</div>
<div class="row mt-5">
<button class="btn btn-primary" id="chartBTN">Next Chart</button>
</div>
</div>
Simple js below. The point is that a chart is generated on load, then, on button click, the chart adds another data set to it for comparison. To do that, I understand I have to destroy the first chart to recreate the second. Testing the button click works, but the chart destroy doesn't do anything. Instead, I get an error.
Am I putting the destroy method in the wrong place?
// Our labels along the x-axis
var years = [1500,1600,1700,1750,1800,1850,1900,1950,1999,2050];
// For drawing the lines
var africa = [86,114,106,106,107,111,133,221,783,2478];
var asia = [282,350,411,502,635,809,947,1402,3700,5267];
var europe = [168,170,178,190,203,276,408,547,675,734];
var latinAmerica = [40,20,10,16,24,38,74,167,508,784];
var northAmerica = [6,3,2,2,7,26,82,172,312,433];
var ctx = $("#myChart");
var myChart = new Chart(ctx, {
type: 'line',
data: {
labels: years,
datasets: [
{
data: africa
}
]
}
});
$('#chartBTN').on('click', function(){
myChart.destroy();
var ctx = $("#myChart");
var myChart = new Chart(ctx, {
type: 'line',
data: {
labels: years,
datasets: [
{
data: africa,
label: 'Africa',
borderColor: "#3e95cd",
fill: false
},
{
data: asia,
label: "Asia",
borderColor: "#3e95cd",
fill: false
}
]
}
});
});
A variable declared with var is hoisted to the top of the function. You are declaring the same variable again in the function.
So the function declaration is hoisted to top of function where it is undefined.
Hence, myChart.destroy() is undefined.
https://developer.mozilla.org/en-US/docs/Glossary/Hoisting
Instead of actually destroying the instance, you should update the instance instead.
myChart.data.datasets = [] //Desired Data
myChart.update();
If you want to go with destroying the instance, you can remove the var declaration from inside the function and it should work fine. (since the variable is already defined in scope of that function.
Related
My goal is to get 3 charts, with the most recently selected series in the first chart updating the second chart, and the most recently selected series in the second chart updating the third chart.
I start by setting up 3 divs:
<div id="chart1"></div>
<div id="chart2"></div>
<div id="chart3"></div>
I then generate 3 charts like so (essentially the same, but with different detail levels in the source data):
var chart1 = c3.generate({
bindto: document.getElementById('chart1'),
data: {
x: 'date',
url: 'file1.csv'
},
axis: {
x: {
type: 'timeseries',
tick: {
format: '%Y-%m-%d',
count: 8
},
label: "Date"
},
y: {
label: "Y Label"
}
},
// irrelevant options removed
legend: {
item: {
onclick: (id) => {
chart1.toggle(id);
chart_options2.data.url = 'file2_'+ id +'.csv';
if(chart1.data.shown(id).length == 1 ){
generateChart2(id);
}
}
}
}
});
var chart2 = c3.generate({
bindto: document.getElementById('chart2'),
// etc.
var chart3 = c3.generate({
bindto: document.getElementById('chart3'),
// etc.
The function generateChart2() will use the id of the selected series in chart1 to generate a new chart in the place of chart2. Likewise I have a similar function generateChart3() which will use the id of the selected series in chart2 to generate a new chart in the place of chart3. These functions look like this:
function generateChart2(id) {
c3.generate(chart_options2);
};
With chart_options being identical to the original set up options, except for the updated file source (see chart_options2.data.url = 'file2_'+ id +'.csv'; above).
Initially, these all seem to work ok. The problem arises when a new chart2 is created using the generateChart2() function, and the onclick property no longer seems to work for certain things. Testing with console.log, the onclick still seems to be called, and I can update the chart options var, but I can't use any of the other commands:
onclick: function (id) {
console.log('test'); // returns 'test'
chart2.toggle(id); // doesn't work
chart_options3.data.url = 'file3_'+ id +'.csv'; // works
if(chart2.data.shown(id).length == 1 ){
generateChart3(id); // doesn't work
}
I suspect it has something to do with the namespace, or how I'm using the chart names, or how I'm calling the function. I've spent a couple of days looking at this problem, but all my research hasn't really made it much clearer which direction I should be looking in.
EDIT: I found a workaround, which makes sense for my use case, but doesn't help me learn what I was doing wrong. Now instead of reusing c3.generate to dynamically create a new chart every time a series is selected in the previous chart, I am using unload() and load(). This seems to be working well, but I would very much appreciate an explanation or clarification of what I was doing wrong the first time. Updated code:
onclick: function (id) {
chart1.toggle(id);
if(chart1.data.shown(id).length == 1 ){
chart2.unload();
setTimeout(function () {
chart2.load({
url: 'file2_'+ id +'.csv'
});
}, 1000);
}
}
I would like to set the colors in a google chart from my code, and not sure how to do it. I have this in a cshtml page.
<script type="text/javascript">
// Load the Visualization API and the piechart package.
//google.load('visualization', '1.0', { 'packages': ['bar'] });
google.load('visualization', '1.0', { 'packages': ['corechart'] });
var visualization;
// Set a callback to run when the Google Visualization API is loaded.
google.setOnLoadCallback(drawCharts);
function drawCharts() {
var titleName = "Rounding Eligible";
$("#chartHeader").html(titleName);
var options = {
'backgroundColor': 'transparent',
title: titleName,
subtitle: 'Range of ddd to ddd', seriesType: "bars",isStacked: true,
series: { 0:{color:"#009add"} ,1:{color:"#009844"} ,2: {color:"#ef7521"} ,3: {color:"#89d2e6"},4:{color:"#82bc00"},5:{color:"#f19f53"},6:{color:"#0055b7"},#(Model.NumSeries) : { type: "line", visibleInLegend: false, color: "#FF0000" }},
vAxis:{title: "Count", minValue:10}
};
// Create the data table.
var data = google.visualization.arrayToDataTable(#Html.Raw(Model.ChartJson));
var chart_div = document.getElementById('chartDiv');
var chart = new google.visualization.ComboChart(chart_div);
chart.draw(data, options);
//setup a temp image to gold hold the chart
createHiddenImage('hiddenCanvas1', 'chartDiv', chart.getImageURI());
}
</script>
What I would like to do is replace my colors ( 0:{color:"#009add"} ,1:{color:"#009844"}) to be based on something in the code and do something like
isStacked: true,
series:
#foreach seriesvalue in #Model.seriesValues
{#Html.Raw(seriesvalue);},
Axis:{title: "Count", minValue:10}
I have no idea what is possible to accomplish this, is it best to just pass the whole options object from the model? Basically I can't figure out how to do it.
Just use JSON serialization:
series: #Html.Raw(JsonConvert.SerializeObject(Model.seriesValues))
You'll want to make seriesValues a Dictionary keyed by the number you want associated with each color.
For a deeper dive, see this answer: Using Razor within JavaScript
You can access properties from your model anywhere on the page, including within the script, via:
#Model.MyPropertyName
With that in mind, your javascript can look something like this:
myColor = '#Model.MyGreenColorProperty';
Note the single quotations around the #Model... this is very important, and will not work if the value is not surrounded by the quotes.
I'm using chart.js, and it's quite handy, but I'm facing an unexpected behavior when I click on arabic. The values change correctly, but if I hover over the bars, it shows the english values, although both of Pages, and views array have the arabic values.
var Pages = [];
var viewers = [];
var keys = [];
var countryvalue = [];
var lang = 'english';
function fillArray(language) {
if (language == 'english') {
Pages = ['home', 'about', 'contact'];
viewers = [5, 2, 3];
} else if (language == 'arabic') {
Pages = ['arabic home', 'arabic about', 'arabic contact'];
viewers = [7, 1, 2];
}
}
function getPages(lang) {
Pages = [];
viewers = [];
fillArray(lang);
drawBar();
}
function drawBar() {
var randomScalingFactor = function() {
return Math.round(Math.random() * 100)
};
var barChartData = {
labels: Pages,
datasets: [{
fillColor: "rgba(151,187,205,0.5)",
strokeColor: "rgba(151,187,205,0.8)",
highlightFill: "rgba(151,187,205,0.75)",
highlightStroke: "rgba(151,187,205,1)",
data: viewers
}]
}
var ctx = document.getElementById("canvas").getContext("2d");
window.myBar = new Chart(ctx).Bar(barChartData, {
responsive: true
});
}
getPages(lang);
$('.languageSwitcher').on('click', function(e) {
e.preventDefault();
if ($(this).data('lang') != lang) {
lang = $(this).data('lang');
getPages(lang);
}
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.0.2/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/1.0.2/Chart.min.js"></script>
<div class="barContainer">
<canvas id="canvas"></canvas>
</div>
<a class="languageSwitcher" data-lang="english">English</a>
<a class="languageSwitcher" data-lang="arabic">Arabic</a>
Here is a fiddle that shows the problem
You need to destroy the existing chart before creating the new one. So something like
var ctx = document.getElementById("canvas").getContext("2d");
if (window.myBar)
window.myBar.destroy();
...
You could also do a similar thing by updating the points and calling update(), but destroy() is easier by far.
Fiddle - https://jsfiddle.net/jzq5umfm/
Note : while removing the existing canvas element and adding a new one would also seemingly work (as mentioned in your comment and the previous version of your question), with responsive: true, Chart.js cycles through all created instances of the graph to resize them, resulting in a console error for each responsive graph that has been created and removed but not destroyed.
For responsive: false you won't see console errors but you'd still have references to those unused instances.
I have a pie chart created using chart.js
Here is the code for that:
var pieChartCanvas = $("#pieChart").get(0).getContext("2d");
var pieChart = new Chart(pieChartCanvas);
var PieData = [
{
value: 700,
color: "#f56954",
highlight: "#f56954",
label: "Chrome"
},
{
value: 500,
color: "#00a65a",
highlight: "#00a65a",
label: "IE"
},
{
value: 400,
color: "#f39c12",
highlight: "#f39c12",
label: "FireFox"
}
pieChart.Doughnut(PieData);
<canvas id="pieChart" style="width:60%; height:60%;"></canvas>
I want to create a few different pie charts representing different information but only want to ever display one pie chart at a time. So when i click on a drop down menu i want another pie chart to appear. Basically using the same code as above but changing the information in the pieData[].
The code for changing using the list is
<ul class="dropdown-menu" aria-labelledby="dropdownMenu2">
<li>Device</li>
</ul>
Is there any way to do this?
Keeping in mind that only data will be changed because you want the same canvas for everything, you can create a function which draw the data on the canvas everytime is called (See the function receive as first parameter the data that will be displayed in the canvas)
function selectData(id){
if(id == 1){
var data = []; //whatever i want to draw when option 1 is selected!
DrawChart(data);
}
if(id == 2){
var data = []; //whatever i want to draw when option 2 is selected!
DrawChart(data);
}
}
function DrawChart(data){
var pieChartCanvas = $("#pieChart").get(0).getContext("2d");
var pieChart = new Chart(pieChartCanvas).Pie(data);
//Demo with official documentation http://www.chartjs.org/docs/
//var myPieChart = new Chart(ctx[0]).Pie(data,options);
//var myDoughnutChart = new Chart(ctx[1]).Doughnut(data,options);
}
so, you are not using a html Select widget, you are using a UL for this, you can use :
<ul class="dropdown-menu" aria-labelledby="dropdownMenu2">
<li>Browsers</li>
<li>Other things</li>
</ul>
Although you can design a better mode to solve your problem, this is a quick solution that will help you to understand how to draw data dinamically with Chart.js
I am using Jquery Flot to create a pie chart based on three different values, pause, nopause and sleeping. Initially it draws th pie chart correctly but after some redraw it gives me the following error.
Could not draw pie with labels contained inside canvas
My code is
Lecturer.socket.onmessage = function (message) {
var str = message.data;
var msg = str.split(":");
if(msg[0] == 'pause'){
var pause = parseInt(msg[1]);
var noPause = parseInt(msg[2]);
var sleeping = parseInt(msg[3]);
var data = [
{label: "Pause", data:pause},
{label: "No Pause", data:noPause},
{label: "Sleeping", data:sleeping}
];
var options = {
series: {
pie: {show: true}
},
legend: {
show: false
}
};
$.plot($("#pie-placeholder"), data, options);
}
};
HTML is
<div id="live-placeholder" class="flot"></div>
All the require js libraries are included. What I m doing wrong? Any Help ?
Thanks
You've got two problems:
1.) your placeholder div id doesn't match the $.plot call. live-placeholder != pie-placeholder.
2.) You don't need to calculate the percents yourself. Flot will do it internally.
See a working fiddle here.