1

Why is my chart attempting to read notifyPlugins('beforeDestroy') when navigating away from the page of the chart?

I have a plugins file, a svelte action and a main file [ChartMetrics.svelte] (where the action is used). The current behaviour is that the chart loads completely fine in the main page, but when navigating to another page I am met with the notifyPlugins error. I have added the most relevant code from my project, as the entire codebase is quite large so would be unable to add it to a codesandbox. Has anyone recieved this error with notifyPlugins before when working with chart.js? If there is any more code you need to see let me know.

console error:

chart.js:6171 Uncaught (in promise) TypeError: Cannot read properties of undefined (reading 'notifyPlugins')
    at destroy (chart.js:6171:1)
    at Object.destroy [as d] (ChartMetrics.svelte:454:1)
    at Object.destroy [as d] (Div.svelte:127:1)
    at destroy_component (index.mjs:1974:1)
    at Object.destroy [as d] (ChartMetrics.svelte:127:1)
    at Object.destroy [as d] (ChartMetrics.svelte:535:1)
    at destroy_component (index.mjs:1974:1)
    at Object.destroy [as d] (EffectiveMaterialAssetsMetricsContainer.svelte:71:1)
    at Object.destroy [as d] (MetricWrapper.svelte:133:1)
    at Object.destroy [as d] (MetricWrapper.svelte:308:1)

chart.js (line 6171):

destroy() {
   this.notifyPlugins('beforeDestroy');
   ....
}

ChartMetrics (line 454):

        d: function destroy(detaching) {
            if (detaching) detach_dev(div0);
            if_blocks[current_block_type_index].d();
            destroy_component(tooltipinfo);
            destroy_component(anchor);
            if (detaching) detach_dev(t3);
            if (detaching) detach_dev(div1);
            mounted = false;
            dispose();
        }

ChartMetrics line 454 (image): chartMetrics

plugins.ts:

export function textPlugin(value: number, theme: Theme) {
  return {
    id: "chartTextPlugin",
    // Plugin for writing text in middle of chart
    beforeDraw: function (chart: ChartType) {
      plugin that draws some text on a chart.js chart
    },
  }
}

export function curvePlugin() {
  // Curve edges of donut
  return {
    id: "chartCurvePlugin",
    afterUpdate: function (chart: ChartType) {
      // some code to curve the edges of the chart.js chart
      }
    },
    afterDraw: function (chart: ChartType) {
      // some more code to do same as above
    },
  }
}

add-chart.ts (svelte action)

export const addChart = (node: HTMLElement, params: ChartProps): ChartGauge => {
  const text = textPlugin(params.value, params.theme)
  const curve = curvePlugin()
  const backgroundColor = mapTypeToColor(params.theme)

  return new Chart(node, {
    type: "doughnut",
    data: {
      datasets: [
        {
          //label: params.caption,
          data: [params.value, 100 - params.value],
          backgroundColor: backgroundColor,
          borderColor: ["rgba(255, 255, 255 ,1)"],
          borderWidth: 0,
        },
      ],
    },
    options: {
      rotation: -90,
      cutout: "85%",
      circumference: 180,
      radius: "85%",
      responsive: true,
      maintainAspectRatio: true,
      aspectRatio: 3,
    },
    plugins: [text, curve],
  })
}

ChartMetrics.svelte

{#if value >= 0 && value <= 100}
  <CardDiv p={4} pl={4} pr={4}>
    <div class="metrics-header">
      <h3>
        Controls -
        {#if titleUrl}
          <Anchor href={titleUrl} size={14}>{title}</Anchor>
        {:else}
          <span>{title}</span>
        {/if}
        <TooltipInfo content={tooltipContent} id="info" width="small" />
      </h3>
      <Anchor href={assuranceUrl} size={14}>View Assurance</Anchor>
    </div>
    <div class="chart">
      <canvas
        id="chart"
        data-test-id="chart"
        use:addChart={{ value, theme }}
        style="height:100%;width:100%;"
      />
    </div>
  </CardDiv>
{:else}
  <MetricsError message="Chart Metrics Widget Error: Invalid value." />
{/if}
9
  • At a first glance, this doesn't appear to be caused by the plugin. It seems that in ChartMetrics.svelte at line 454 there's a call to chart's destroy that makes this undefined inside the function, something like chart.destroy.call() instead of chart.destroy() Commented May 27, 2023 at 4:28
  • Yep thats in chartjserror picture I had linked. Would this be an issue with chartjs then? Commented May 28, 2023 at 23:38
  • No, I think the cause is the way that code from the svelte component ChartMetrics calls destroy. Can't you post what's there at around line 454? Commented May 29, 2023 at 1:34
  • Sure, i edited my question to put the full code where destroy is called. Commented May 29, 2023 at 2:45
  • 1
    Yep its a component of my code. I will edit the answer with as much of the code as i can and replace images with code Commented May 29, 2023 at 5:49

1 Answer 1

0

You could try this patch of a solution: in add-chart.ts instead of

return new Chart(node, {
.....
});

use

const chart = new Chart(node, {
    .....
});
chart.destroy = chart.destroy.bind(chart);
return chart; 

A more svelte-idiomatic way might be

return {
   ...chart,
   destroy(){
      chart.destroy();
   }
}

but that would probably need some typescript acrobatics to pass type-checking.

Sign up to request clarification or add additional context in comments.

8 Comments

@ou9hwq93weiq please let me know what's happening if you make the first change; we are on trial and error path here
Hey sorry I just got back to the computer. I just tried out your solution and it works! Could you explain whats happening with the svelte-idiomatic way? Are you ovewriting the destroy function for the ChartMetrics file and replacing it with the charts destroy function?
Just to clarify, both methods work but just want to understand whats going on when you say "chart.destroy = chart.destroy.bind(chart)"?
So the problem was that the default destroy method generated by svelte (in your generated file at line 381) just took the destroy method from the chart instance and called it with a undefined context (this), see the source code.
The first method uses bind, so with that, the destroy function is always linked to the chart instance, no matter how it is called, this will be that instance.
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.