I'm using this tutorial to draw charts using chart.js in Wix Valo
I only placed a new button that changes the chart data like :
if (!chart1 ) {chart1 = new ChartJSAPI($w('#CustomElement1'));}
chart1.customization = data_chart.customization
chart1.data = data_chart.data
}
When I clicked on the preview button the chart refreshed well, but if I published it I got this error (I'm a premium customer)
hart.js:5552 Uncaught Error: Canvas is already in use. Chart with ID '0' must be destroyed before the canvas with ID 'myChart' can be reused.
How to solve this?
The all code
import { ChartJSAPI } from 'public/chart-api';
import { chartCustomization } from 'public/chart-customization';
import wixData from 'wix-data';
$w.onReady(async function () {
chart(processDataForChart(results2))
})
var results2 = [
{
trip_date: {
value: "2021-01-01"
},
trips_count: 19595
},
{
trip_date: {
value: "2021-02-01"
},
trips_count: 19460
},
{
trip_date: {
value: "2021-03-01"
},
trips_count: 10909
},
{
trip_date: {
value: "2021-04-01"
},
trips_count: 35
}
];
var results3 = [
{
trip_date: {
value: "2021-01-01"
},
trips_count: 1955
},
{
trip_date: {
value: "2021-02-01"
},
trips_count: 1960
},
{
trip_date: {
value: "2021-03-01"
},
trips_count: 1009
},
{
trip_date: {
value: "2021-04-01"
},
trips_count: 3570
}
];
// This function is determine which column will be X
function getColumnName(jsonData) {
var columns_counter = 0
var columns = Object.keys(jsonData[0])
for (var i = 0; i < columns.length; i++) {
// var value = jsonData[0][column];
// console.log('', typeof (jsonData[0][columns[i]])
if (jsonData[0][columns[i]].value != undefined) {
console.log('getColumnName',columns[i])
return columns[i];
}
columns_counter ++
}
console.log('getColumnName',columns[0])
columns[0]
}
function processDataForChart(jsonData) {
var columns = Object.keys(jsonData[0])
var dataArray = [];
var counter = 0
var labels = [];
var columns_counter = 0
var backgroundColor1 = ["rgba(255, 99, 132, 0.2)","rgba(54, 162, 235, 0.2)","rgba(255, 206, 86, 0.2)","rgba(75, 192, 192, 0.2)","rgba(255, 159, 64, 1)"]
var borderColor1 = ["rgba(255, 99, 132, 1)", "rgba(54, 162, 235, 1)","rgba(255, 206, 86, 1)","rgba(75, 192, 192, 1)","rgba(153, 102, 255, 1)","rgba(255, 159, 64, 1)"]
var backgroundC = []
var borderC = []
var x_axis = getColumnName(jsonData)
columns.forEach(function(column) {
console.log('column',column )
console.log('x_axis', x_axis )
if(column != x_axis ){
var label = [];
var type=[];
var data = [];
var backgroundC = [];
var borderC = [];
var borderWidth = [];
// console.log('columns',columns)
backgroundC.push(backgroundColor1[columns_counter]);
borderC.push(borderColor1[columns_counter]);
type.push("bar")
borderWidth.push(1)
// columns_counter ++
jsonData.forEach(function(item) {
if (counter < 5){
// console.log('item[column]',item[column])
// if(item[column].value == undefined){
data.push(item[column]);
// ;
// } else{
// }
}
});
dataArray.push({
label: column,
type: type,
data: data,
backgroundColor: backgroundC,
borderColor: borderC,
borderWidth: borderWidth
});
counter++; // Increment counter after processing a column
}
else{
jsonData.forEach(function(item) {
labels.push(item[column].value);
})
}
columns_counter ++
})
// var output = '"labels":'+ labels + ',"datasets":' + dataArray
// console.log('dataArray',dataArray)
// return output
var data_charts = {
data: {
"labels": labels,
"datasets": dataArray
},
customization: {
"borderWidth": 1,
}
};
return data_charts
}
var chart1
async function chart(data_chart){
// if(chart1 == undefined){
if (!chart1 ) {chart1 = new ChartJSAPI($w('#CustomElement1'));}
chart1.customization = data_chart.customization
chart1.data = data_chart.data
}
export function button6_click(event) {
chart(processDataForChart(results3))
}
Here is a minimal set of changes to the velo chart.js tutorial referenced in the question, that would allow for the update of the chart. The tutorial consists in four files:
Chart.JS, which is the frontend, i.e., the javascript of the web page;chart-api.jsand achart-customization.jswhich are part of the backend, and might be placed in the wix app's/public/directorychart.js, which is the class defining the custom element displaying the chart; it might be placed in/public/custom-elementsThe changes are to be made to the two
chart.js/Chart.JSfiles; there are no changes required forchart-api.jsorchart-customization.jsTo illustrate the dynamic data version we'll suppose a button (
button1) was added to the page,that has an click handler called
button1_click. Also, there's a collection"ChartItems2", that has the same labels as"ChartItems", but different values for the data. On pressingbutton1, we want the chart to update displaying the data from"ChartItems2", that means the bars of the chart will have different lengths.The frontend
The standard method to update the frontend is assign new data to the chart object (instance of
ChartJSAPI) and call its.render()method. This means that we have to keep track of this object, through a global variable; Here's the updatedChart.JSfile:The custom element class
The changes above are the only required modifications in "Preview" mode; however, for the "published" site, one gets the error
Canvas is already in use. Chart with ID '0' must be destroyed before the canvas with ID 'myChart' can be reused., as reported in the question.To address this issue one has to make some adjustments to the
chart.jscustom element class. Here, one has to keep track of the chart.js instance, the result ofnew Chart(...); we assign this object to the property_chartof theChartElemclass; we initialize it withnullin the constructor:Then, in the
rendermethod, we verify first ifthis._chartis initialized and if it is, we destroy the current chart first:Also note the change in the
scalesoptions, required to make the code compatible with newer versions of chart.js. There are no changes to the other method of the classChartElem.Alternatively, we can use chart.js method
updatemethod; it has the advantage that it doesn't recreate the chart:If we know that only the values of the first (and only) dataset are changed, we can replace
with
which results in a smooth transition from the first chart view to the second, the bars being animated from their first position to the second. That happens if the y-axis doesn't have to be rescaled, which means that either the maximum y value is the same, or, the option
options.scales.y.maxis set to a value that is greater than both the maximum values (the one before and the one after the update).