I've run into a problem with employing your financial chart module together with another canvas displaying a chart from chart.js.
The browser console points the "Uncaught TypeError: Class extends value undefined is not a constructor or null" error
to line 235 in chartjs-chart-financial.js:
class FinancialController extends Chart.BarController { ... }
Here are two templates being used in node red which are loaded into the front end. Both reference to individual canvasses with different IDs. (As you will see below, I try to destroy any previously existing chart instance)
First script:
<script src="https://cdn.jsdelivr.net/npm/luxon@1.24.1"></script>
<script src="https://cdn.jsdelivr.net/npm/chart.js@3.0.0-beta.9/dist/chart.js"></script>
<script src="https://cdn.jsdelivr.net/npm/chartjs-adapter-luxon@0.2.1"></script>
<script src="../chartjs-chart-financial.js" type="text/javascript"></script>
<div style="width:1000px">
<canvas id="candleStickChart"></canvas>
</div>
<div>
Bar Type:
<select id="type">
<option value="candlestick" selected>Candlestick</option>
<option value="ohlc">OHLC</option>
</select>
Scale Type:
<select id="scale-type">
<option value="linear" selected>Linear</option>
<option value="logarithmic">Logarithmic</option>
</select>
Color Scheme:
<select id="color-scheme">
<option value="muted" selected>Muted</option>
<option value="neon">Neon</option>
</select>
Border:
<select id="border-color">
<option value="true" selected>Yes</option>
<option value="false">No</option>
</select>
<button id="update">Update</button>
<button id="randomizeData">Randomize Data</button>
</div>
<script>
(function(scope) {
var timer = setInterval(function() { //check that the luxon and other libs are loaded, if not wait
if (!window.luxon) return;
clearInterval(timer);
var barCount = 60;
var initialDateStr = '01 Apr 2017 00:00 Z';
debugger;
var ctx = document.getElementById('candleStickChart').getContext('2d');
ctx.canvas.width = 1000;
ctx.canvas.height = 250;
if (chart)
chart.destroy();
var chart = new Chart(ctx, {
type: 'candlestick',
data: {
datasets: [{
label: 'CHRT - Chart.js Corporation',
data: getRandomData(initialDateStr, barCount)
}]
}
});
var getRandomInt = function(max) {
return Math.floor(Math.random() * Math.floor(max));
};
function randomNumber(min, max) {
return Math.random() * (max - min) + min;
}
function randomBar(date, lastClose) {
var open = randomNumber(lastClose * 0.95, lastClose * 1.05).toFixed(2);
var close = randomNumber(open * 0.95, open * 1.05).toFixed(2);
var high = randomNumber(Math.max(open, close), Math.max(open, close) * 1.1).toFixed(2);
var low = randomNumber(Math.min(open, close) * 0.9, Math.min(open, close)).toFixed(2);
return {
t: date.valueOf(),
o: open,
h: high,
l: low,
c: close
};
}
function getRandomData(dateStr, count) {
var date = luxon.DateTime.fromRFC2822(dateStr);
var data = [randomBar(date, 30)];
while (data.length < count) {
date = date.plus({days: 1});
if (date.weekday <= 5) {
data.push(randomBar(date, data[data.length - 1].c));
}
}
return data;
}
var update = function() {
var dataset = chart.config.data.datasets[0];
// candlestick vs ohlc
var type = document.getElementById('type').value;
dataset.type = type;
// linear vs log
var scaleType = document.getElementById('scale-type').value;
chart.config.options.scales.y.type = scaleType;
// color
var colorScheme = document.getElementById('color-scheme').value;
if (colorScheme === 'neon') {
dataset.color = {
up: '#01ff01',
down: '#fe0000',
unchanged: '#999'
};
} else {
delete dataset.color;
}
var border = document.getElementById('border-color').value;
var defaultOpts = Chart.defaults.elements[type];
if (border === 'true') {
dataset.borderColor = defaultOpts.borderColor;
} else {
dataset.borderColor = {
up: defaultOpts.color.up,
down: defaultOpts.color.down,
unchanged: defaultOpts.color.up
};
}
chart.update();
};
document.getElementById('update').addEventListener('click', update);
document.getElementById('randomizeData').addEventListener('click', function() {
chart.data.datasets.forEach(function(dataset) {
dataset.data = getRandomData(initialDateStr, barCount);
});
update();
});
}, 100); // close out the setInterval
})(scope);
</script>
Second script:
<script>
//(function(scope) {
var ctx = document.getElementById("avgPriceChangesChart");
var numberOfSignals = '{{flow.selected_symbol_avgByNumberOfSignals}}';
var shortestPriceSeries = '{{flow.selected_symbol_shortestPriceSeries}}';
var omittedPriceSeries = '{{flow.selected_symbol_omittedPriceSeries}}';
var symbol = '{{flow.selected_symbol}}';
var demoData = {
labels: ["D1", "D2", "D3", "D4", "D5", "D6"],
datasets: [
{
label: "Signal 1",
backgroundColor: 'rgb(255, 99, 132)',
data: [0.2,-0.4,0.6,0.9,0.4,0.5]
},
{
label: "Signal 2",
backgroundColor: 'rgb(132, 99, 255)',
data: [-0.3,0.5,0.6,0.4,0.2,0.6]
},
{
label: "Signal 3",
backgroundColor: 'rgb(132, 255, 99)',
data: [0.1,-0.1,0.4,-0.2,0.4,0.8]
}
]
};
function createChart(someData) {
var avgPriceChangesChart = new Chart(ctx, {
type: "bar",
data: someData,
options: {
animation: {duration: 0},
title: {
responsive: true,
display: true,
text: "Averaged Price Changes (%) after " + numberOfSignals + " signals occurred in " + symbol
+ ".\nShortest price series: " + shortestPriceSeries + "\n"
+ ".\nOmitted price series (due to <20 price changes): " + omittedPriceSeries + "\n",
fontSize: 16,
fontColor: 'rgb(255,255,255)'
},
legend: {
display: true,
fontColor: 'white',
position: 'top'
},
maintainAspectRatio: false,
scales: {
xAxes: [{
barPercentage: 1.0,
barThickness: 6, // number (pixels) or 'flex'
maxBarThickness: 8, // number (pixels)
stacked: false,
gridLines: {
color: 'rgb(96,96,96)',
display: true
},
ticks: {
fontColor: 'white',
autoSkip: false
}
}],
yAxes: [{
stacked: false,
gridLines: {
color: 'rgb(96,96,96)',
display: true
},
ticks: {
beginAtZero: true,
fontColor: 'white',
min: -1,
max: 1
}//,
/*type: 'logarithmic'
afterBuildTicks: function (chartObj) { //Build ticks labelling as per your need
chartObj.ticks = [];
chartObj.ticks.push(0.01);
chartObj.ticks.push(0.1);
chartObj.ticks.push(1);
chartObj.ticks.push(10);
chartObj.ticks.push(100);
}//*/
}]
}
}
});
return avgPriceChangesChart;
}
/*if (avgPriceChangesChart !== undefined && Boolean(avgPriceChangesChart)) {
//alert("Trying to destroy old chart instance...");
debugger;
avgPriceChangesChart.destroy();
}*/
// for simplicity create a chart with simple demo data only. Nothing fancy.
avgPriceChangesChart = createChart(demoData); //msg.payload.data);
/*scope.$watch('msg', function(msg) {
if (msg) {
if (avgPriceChangesChart !== undefined && Boolean(avgPriceChangesChart)) {
//alert("Trying to destroy old chart instance...");
debugger;
avgPriceChangesChart.destroy();
}*/
/* debugger;
avgPriceChangesChart = createChart(msg.payload.data);
//avgPriceChangesChart.data.datasets[0].data[2] = 50; // Would update the first dataset's value of 'March' to be 50
//avgPriceChangesChart.data = msg.payload.data;
//avgPriceChangesChart["data"] = msg.payload.data;
avgPriceChangesChart.update();
}
});*/
//}) (scope);
</script>
The whole stack trace from the console looks as follows:
app.min.js:20 Uncaught TypeError: Class extends value undefined is not a constructor or null
at <anonymous>:238:41
at <anonymous>:14:76
at <anonymous>:15:2
at b (app.min.js:20)
at Function.globalEval (app.min.js:20)
at Object.dataFilter (app.min.js:20)
at app.min.js:20
at l (app.min.js:20)
at XMLHttpRequest.<anonymous> (app.min.js:20)
at Object.send (app.min.js:20)
....
VM2648 chart.js:5161 Uncaught Error: "candlestick" is not a registered controller.
at Registry._get (VM2648 chart.js:5161)
at Registry.getController (VM2648 chart.js:5106)
at Chart.buildOrUpdateControllers (VM2648 chart.js:6188)
at Chart.update (VM2648 chart.js:6227)
at new Chart (VM2648 chart.js:5988)
at <anonymous>:20:21
_get @ VM2648 chart.js:5161
getController @ VM2648 chart.js:5106
buildOrUpdateControllers @ VM2648 chart.js:6188
update @ VM2648 chart.js:6227
Chart @ VM2648 chart.js:5988
(anonymous)
VM2648 chart.js:5945 Uncaught Error: Canvas is already in use. Chart with ID '0' must be destroyed before the canvas can be reused.
at new Chart (VM2648 chart.js:5945)
at <anonymous>:20:21
Chart @ VM2648 chart.js:5945
(anonymous)
VM2648 chart.js:5161 Uncaught Error: "candlestick" is not a registered controller.
at Registry._get (VM2648 chart.js:5161)
at Registry.getController (VM2648 chart.js:5106)
at Chart.buildOrUpdateControllers (VM2648 chart.js:6188)
at Chart.update (VM2648 chart.js:6227)
at Chart._resize (VM2648 chart.js:6053)
at Chart.resize (VM2648 chart.js:6027)
at listener (VM2648 chart.js:6509)
at VM2648 chart.js:1776
at VM2648 chart.js:34
I would be very much appreciate any hints to what I'm doing wrong.
Hi mate,
I've run into a problem with employing your financial chart module together with another canvas displaying a chart from chart.js.
The browser console points the "Uncaught TypeError: Class extends value undefined is not a constructor or null" error to line 235 in chartjs-chart-financial.js:
class FinancialController extends Chart.BarController { ... }
Here are two templates being used in node red which are loaded into the front end. Both reference to individual canvasses with different IDs. (As you will see below, I try to destroy any previously existing chart instance)
First script:
Second script:
The whole stack trace from the console looks as follows:
I would be very much appreciate any hints to what I'm doing wrong.
Cheers, Marcel