Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Using classic chart from Chart.js and a financial chart: Uncaught TypeError: Class extends value undefined is not a constructor or null #92

Open
CreativeWarlock opened this issue Feb 27, 2021 · 0 comments

Comments

@CreativeWarlock
Copy link

CreativeWarlock commented Feb 27, 2021

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:

<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.

Cheers,
Marcel

@CreativeWarlock CreativeWarlock changed the title Uncaught TypeError: Class extends value undefined is not a constructor or null Using classic chart from Chart.js and a financial chart: Uncaught TypeError: Class extends value undefined is not a constructor or null Feb 27, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant