chartjs / chartjs-plugin-annotation

Annotation plugin for Chart.js
MIT License
606 stars 328 forks source link

Bug when using the plugin with Vue #796

Closed enertisbd closed 2 years ago

enertisbd commented 2 years ago

I am getting the error below when I just register the plugin. There is no need to use the plugin, only by registering you will get the error. Error in mounted hook: "RangeError: Maximum call stack size exceeded"

Chart.js v3.9.1 chartjs-plugin-annotation@2.0.1 (tried previous versions, but now success.)

stockiNail commented 2 years ago

Are you able to provide a reproducible example of the error? I have done some simple tests and sounds work.

rodindev commented 2 years ago

Are you able to provide a reproducible example of the error? I have done some simple tests and sounds work.

Same problem in Nuxt. Chart.js is added to the project like this:

/plugins/chart.js

import Vue from 'vue';
import { Line } from 'vue-chartjs/legacy';
import annotationPlugin from 'chartjs-plugin-annotation';
import {
  Chart as ChartJS,
  Title,
  Tooltip,
  Legend,
  BarElement,
  CategoryScale,
  LinearScale,
  LineElement,
  PointElement,
} from 'chart.js';

ChartJS.register(
  Title,
  Tooltip,
  Legend,
  PointElement,
  BarElement,
  CategoryScale,
  LinearScale,
  LineElement,
  annotationPlugin,
);

Vue.component('line-chart', {
  extends: Line,
});

nuxt.config.js

plugins: [
    { src: "~plugins/aos.js", ssr: false },
    { src: '~plugins/vue-carousel.js', mode: 'client' },
    { src: '~/plugins/chart.js', mode: 'client' },
],

It works without annotationPlugin.

stockiNail commented 2 years ago

@rodindev can you provide a codesandbox sample to reproduce the issue?

rodindev commented 2 years ago

@stockiNail https://codesandbox.io/s/wonderful-cori-4ck2or?file=/components/Tutorial.vue

stockiNail commented 2 years ago

@rodindev it is weird.. I had a look and it seems there is a loop in the helpers of CHART.JS:

InternalError: too much recursion
    ownKeys helpers.segment.mjs:1772

I'm still investigating

stockiNail commented 2 years ago

@rodindev First of all, the issue is not on the plugin registration. This is working well. The issue is when the chart is drawing. I think I have found something interesting.

vue-chartjs is checking if the annotation plugin is "configured"

https://github.com/apertureless/vue-chartjs/blob/2a20eb8cdc1834e2ca4478c5b9a7be8c88d77737/legacy/src/Charts.js#L77-L82

And it's weird but I don't know how it was implemented.

Therefore I have added the annotation plugins config in the chart options in Tutorial.vue

<script>
export default {
  data() {
    console.log("return data");
    return {
      chartData: {
        labels: ["1", "2", "3", "4", "5", "6", "7"],
        datasets: [
          {
            label: "Dataset",
            data: [65, 59, 80, 81, 56, 55, 40],
            //fill: false,
            borderColor: "rgb(75, 192, 192)",
            //tension: 0.1,
          },
        ],
      },
      chartOptions: {
        plugins: {  // <-- ADDED!!!
          annotation: {
            annotations: {
              d: {
                type: "label",
                xValue: "6",
                yValue: 60,
                content: "Annotation!!",
              },
            },
          },
        },
      },
    };
  },
};
</script>

and it works, without any issue:

https://codesandbox.io/s/jolly-forest-d0c6qh

Maybe I'm wrong but I think the issue is in vue-chartjs. ASA I'll have more time, I'll try to understand more why the recursive loop is happening if the plugin config is not set.

rodindev commented 2 years ago

@stockiNail I should have tried this... It works in my project! Thank you!

stockiNail commented 2 years ago

@rodindev @enertisbd There is an issue in vue-chartjs related to this: https://github.com/apertureless/vue-chartjs/issues/846.

And here the comment where the solution was the same I found: https://github.com/apertureless/vue-chartjs/issues/846#issuecomment-1149575966

I think there isn't anything else we can do :(, I guess. Let me know if this is working for you so we can close the issue.

markkimsal commented 1 year ago

For anyone still running into this:

This is a problem with Vue reactivity and Chart.js's use of Proxy objects. The infinite loop is exposed by the vue-chartjs plugin, but can happen with regular Vue and regular chart.js. The likelihood is increased if using the annotations plugin, because of the annotation's animation config settings being the main culprit that trips Vue's reactivity into an infinite loop.

The solution is to keep Vue's reactivity away from the chart instance, and only have reactivity on the data, options, and config that feed the chart instance. Create your chart instance as a const outside of the Vue component data, or wrap the chart instance with markRaw from Vue.

Here is my patch for Vue 2.7 which can help show what's going on. I'm not sure if this is a Vue 2.7 only problem or if it is fixed in 3, but I doubt Vue will pick-up this change when you can wrap your chart.js component with markRaw()

/**
 * Attempt to create an observer instance for a value,
 * returns the new observer if successfully observed,
 * or the existing observer if the value already has one.
 */
function observe(value, shallow, ssrMockReactivity) {
    if (value && hasOwn(value, '__ob__') && value.__ob__ instanceof Observer) {
        return value.__ob__;
    }
    if (shouldObserve &&
        (ssrMockReactivity || !isServerRendering()) &&
        (isArray(value) || isPlainObject(value)) &&
        Object.isExtensible(value) &&
        !value.__v_skip /* ReactiveFlags.SKIP */ &&
        !isRef(value) &&
+       !(isProxy(value)) &&
        !(value instanceof VNode)) {
        return new Observer(value, shallow, ssrMockReactivity);
    }
}