diff --git a/src/Chartjs/assets/dist/controller.js b/src/Chartjs/assets/dist/controller.js
index 76f0db9fe7c..c865bf75b77 100644
--- a/src/Chartjs/assets/dist/controller.js
+++ b/src/Chartjs/assets/dist/controller.js
@@ -1,12 +1,19 @@
import { Controller } from '@hotwired/stimulus';
import Chart from 'chart.js/auto';
+let isChartInitialized = false;
class default_1 extends Controller {
constructor() {
super(...arguments);
this.chart = null;
}
connect() {
+ if (!isChartInitialized) {
+ isChartInitialized = true;
+ this.dispatchEvent('init', {
+ Chart,
+ });
+ }
if (!(this.element instanceof HTMLCanvasElement)) {
throw new Error('Invalid element');
}
@@ -14,7 +21,10 @@ class default_1 extends Controller {
if (Array.isArray(payload.options) && 0 === payload.options.length) {
payload.options = {};
}
- this.dispatchEvent('pre-connect', { options: payload.options });
+ this.dispatchEvent('pre-connect', {
+ options: payload.options,
+ config: payload,
+ });
const canvasContext = this.element.getContext('2d');
if (!canvasContext) {
throw new Error('Could not getContext() from Element');
diff --git a/src/Chartjs/assets/src/controller.ts b/src/Chartjs/assets/src/controller.ts
index 12e10e8e316..8b9ce1c1403 100644
--- a/src/Chartjs/assets/src/controller.ts
+++ b/src/Chartjs/assets/src/controller.ts
@@ -12,6 +12,8 @@
import { Controller } from '@hotwired/stimulus';
import Chart from 'chart.js/auto';
+let isChartInitialized = false;
+
export default class extends Controller {
declare readonly viewValue: any;
@@ -22,6 +24,13 @@ export default class extends Controller {
private chart: Chart | null = null;
connect() {
+ if (!isChartInitialized) {
+ isChartInitialized = true;
+ this.dispatchEvent('init', {
+ Chart,
+ });
+ }
+
if (!(this.element instanceof HTMLCanvasElement)) {
throw new Error('Invalid element');
}
@@ -31,7 +40,10 @@ export default class extends Controller {
payload.options = {};
}
- this.dispatchEvent('pre-connect', { options: payload.options });
+ this.dispatchEvent('pre-connect', {
+ options: payload.options,
+ config: payload,
+ });
const canvasContext = this.element.getContext('2d');
if (!canvasContext) {
diff --git a/src/Chartjs/assets/test/controller.test.ts b/src/Chartjs/assets/test/controller.test.ts
index ed26b40d148..8c7512b0118 100644
--- a/src/Chartjs/assets/test/controller.test.ts
+++ b/src/Chartjs/assets/test/controller.test.ts
@@ -13,9 +13,18 @@ import { Application } from '@hotwired/stimulus';
import { waitFor } from '@testing-library/dom';
import ChartjsController from '../src/controller';
+// Kept track of globally, but just used in one test.
+// This is because, by the time that test has run, it is likely that the
+// chartjs:init event has already been dispatched. So, we capture it out here.
+let initCallCount = 0;
+
const startChartTest = async (canvasHtml: string): Promise<{ canvas: HTMLCanvasElement, chart: Chart }> => {
let chart: Chart | null = null;
+ document.body.addEventListener('chartjs:init', () => {
+ initCallCount++;
+ });
+
document.body.addEventListener('chartjs:pre-connect', () => {
document.body.classList.add('pre-connected');
});
@@ -95,4 +104,51 @@ describe('ChartjsController', () => {
expect(chart.options.showLines).toBe(true);
});
});
+
+ it('dispatches the events correctly', async () => {
+ let preConnectCallCount = 0;
+ let preConnectDetail: any = null;
+
+ document.body.addEventListener('chartjs:pre-connect', (event: any) => {
+ preConnectCallCount++;
+ preConnectDetail = event.detail;
+ });
+
+ await startChartTest(`
+
+ `);
+ expect(initCallCount).toBe(1);
+ expect(preConnectCallCount).toBe(1);
+ expect(preConnectDetail.options.showLines).toBe(false);
+ expect(preConnectDetail.config.type).toBe('line');
+ expect(preConnectDetail.config.data.datasets[0].data).toEqual([0, 10, 5, 2, 20, 30, 45]);
+
+ // add a second chart!
+ const canvas = document.createElement('canvas');
+ canvas.dataset.controller = 'chartjs';
+ canvas.dataset.chartjsViewValue = JSON.stringify({
+ type: 'line',
+ data: {
+ labels: ['January', 'February', 'March', 'April', 'May', 'June', 'July'],
+ datasets: [{
+ label: 'My First dataset',
+ backgroundColor: 'rgb(255, 99, 132)',
+ borderColor: 'rgb(255, 99, 132)',
+ data: [0, 10, 5, 2, 20, 30, 45],
+ }],
+ },
+ options: {
+ showLines: false,
+ },
+ });
+ document.body.appendChild(canvas);
+
+ await waitFor(() => expect(preConnectCallCount).toBe(2));
+ // still only initialized once
+ expect(initCallCount).toBe(1);
+ });
});
diff --git a/src/Chartjs/doc/index.rst b/src/Chartjs/doc/index.rst
index 56ded2a3f18..5baa79a125c 100644
--- a/src/Chartjs/doc/index.rst
+++ b/src/Chartjs/doc/index.rst
@@ -98,18 +98,20 @@ First, install the plugin:
$ npm install chartjs-plugin-zoom -D
# or use yarn
- $ yarn add chartjs-plugin-zoom -dev
+ $ yarn add chartjs-plugin-zoom --dev
Then register the plugin globally. This can be done in your ``app.js`` file:
.. code-block:: javascript
// assets/app.js
-
- import { Chart } from 'chart.js';
import zoomPlugin from 'chartjs-plugin-zoom';
- Chart.register(zoomPlugin);
+ // register globally for all charts
+ document.addEventListener('chartjs:init', function (event) {
+ const Chart = event.detail.Chart;
+ Chart.register(zoomPlugin);
+ });
// ...
@@ -177,10 +179,11 @@ custom Stimulus controller:
_onPreConnect(event) {
// The chart is not yet created
- console.log(event.detail.options); // You can access the chart options using the event details
+ // You can access the config that will be passed to "new Chart()"
+ console.log(event.detail.config);
// For instance you can format Y axis
- event.detail.options.scales = {
+ event.detail.config.options.scales = {
yAxes: [
{
ticks: {
@@ -213,6 +216,24 @@ Then in your render call, add your controller as an HTML attribute:
{{ render_chart(chart, {'data-controller': 'mychart'}) }}
+There is also a ``chartjs:init`` event that is called just *one* time before your
+first chart is rendered. That's an ideal place to `register plugins globally `_
+or make other changes to any "static"/global part of Chart.js. For example,
+to add a global `Tooltip positioner`_:
+
+.. code-block:: javascript
+
+ // assets/app.js
+
+ // register globally for all charts
+ document.addEventListener('chartjs:init', function (event) {
+ const Chart = event.detail.Chart;
+ const Tooltip = Chart.registry.plugins.get('tooltip');
+ Tooltip.positioners.bottom = function(items) {
+ /* ... */
+ };
+ });
+
Backward Compatibility promise
------------------------------
@@ -228,3 +249,4 @@ the Symfony framework: https://symfony.com/doc/current/contributing/code/bc.html
.. _`a lot of plugins`: https://github.com/chartjs/awesome#plugins
.. _`zoom plugin`: https://www.chartjs.org/chartjs-plugin-zoom/latest/
.. _`zoom plugin documentation`: https://www.chartjs.org/chartjs-plugin-zoom/latest/guide/integration.html
+.. _`Tooltip positioner`: https://www.chartjs.org/docs/latest/samples/tooltip/position.html