Getting an error message trying to use Chart.js[Twine 2.4.0][Sugarcube 2.36.1]

So, while trying to load Chart.js into Sugarcube 2 I ended up getting the following error message.

A fatal error has occurred. Aborting.
Error: Chart is not defined.

Funnily enough, despite the error, the sample chart I was using for testing purposes still loaded, which meant that it’s probably something I did wrong. Here’s how I have been doing this. I’m going to guess that it’s because the instructions say to use the setup.JSLoaded to check if the library has finished loading. But I don’t know how to have the code run only after everything is loaded.

I first loaded the Chart.js using HiEv’s loading External Script’s

setup.JSLoaded = false;
var lockID = LoadScreen.lock();  // Lock loading screen
importScripts("https://cdn.jsdelivr.net/npm/chart.js")  // Your JavaScript files go here
	.then(function() {
		setup.JSLoaded = true;
		// Reload current passage since imported scripts can function now.
		Engine.play(passage(), true);
		LoadScreen.unlock(lockID);  // Unlock loading screen
	}).catch(function(error) {
		alert(error);
	}
);

Then I have the following Chart being loaded on the Start Passage with an <<include [[Passage]]>> Macro. I have used the instructions from the following link to get it working in the first place.

The actual chart is of the example ones in Chart.js Documentation page.

<div>
  <canvas id="myChart"></canvas>
</div>



<<script>>
	$(document).one(":passageend", function (event) {
		var ctx = document.getElementById("myChart").getContext("2d");
		var myChart = new Chart(ctx, {
    type: 'bar',
    data: {
        labels: ['Red', 'Blue', 'Yellow', 'Green', 'Purple', 'Orange'],
        datasets: [{
            label: '# of Votes',
            data: [12, 19, 3, 5, 2, 3],
            backgroundColor: [
                '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(153, 102, 255, 0.2)',
                'rgba(255, 159, 64, 0.2)'
            ],
            borderColor: [
                '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)'
            ],
            borderWidth: 1
        }]
    },
    options: {
        scales: {
            y: {
                beginAtZero: true
            }
        }
    }
});
	});
	
<</script>>

So, I can sort of tell where I went wrong, but don’t really know how to fix it. I’d be grateful if anyone can help.

The basic issue stems from HiEv’s code not waiting for the network load to finish, which leads to the initial error because Chart doesn’t yet exist. After the load completes, their code then replays the starting passage, which allows the chart to work but only after the initial error.

That’s really not how you should be doing that sort of thing. I provide an alternative method below.


I suggest something like the the following (goes in your Story JavaScript):

/* Import chart.js and retain the returned `Promise`. */
setup.chartImport = importScripts('https://cdn.jsdelivr.net/npm/chart.js');

/* Lock the loading screen, until all imports are settled. */
var lockId = LoadScreen.lock();

/* Set then and catch callbacks on the import `Promise`. */
setup.chartImport
	.then(function () {
		/* Unlock the loading screen. */
		LoadScreen.unlock(lockId);
	})
	.catch(function (err) {
		/* Unlock the loading screen. */
		LoadScreen.unlock(lockId);

		/* Report the error to the user. */
		var mesg = 'Error: unable to load external script (chart.js): ' + String(err);
		UI.alert(mesg);
		console.error(mesg);
	});

Then try the following altered code in your passage:

<div>
  <canvas id="myChart"></canvas>
</div>

<<script>>
setTimeout(function () {
	setup.chartImport.then(function () {
		var ctx = document.getElementById('myChart').getContext('2d');
		var myChart = new Chart(ctx, {
			type: 'bar',
			data: {
				labels: ['Red', 'Blue', 'Yellow', 'Green', 'Purple', 'Orange'],
				datasets: [{
					label: '# of Votes',
					data: [12, 19, 3, 5, 2, 3],
					backgroundColor: [
						'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(153, 102, 255, 0.2)',
						'rgba(255, 159, 64, 0.2)'
					],
					borderColor: [
						'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)'
					],
					borderWidth: 1
				}]
			},
			options: {
				scales: {
					y: {
						beginAtZero: true
					}
				}
			}
		});
	});
}, 25);
<</script>>

EDIT: Added an explanation to the top.

1 Like

Thanks for the help. This is working as intended, once I fixed the typo here.

.catch(funvtion (err) {
		/* Unlock the loading screen. */
		LoadScreen.unlock(lockId);

Should be function with a c. Only pointing it out in case someone else tries to use this solution and misses that. Otherwise, once again, thanks for the help.