Не удается прочитать свойства неопределенного (чтение «notifyPlugins») — chart.js

Почему моя диаграмма пытается прочитать notifyPlugins('beforeDestroy') при переходе со страницы диаграммы?

У меня есть файл плагинов, действие svelte и основной файл [ChartMetrics.svelte] (где используется действие). Текущее поведение заключается в том, что диаграмма полностью загружается на главной странице, но при переходе на другую страницу я встречаюсь с ошибкой notifyPlugins. Я добавил наиболее подходящий код из своего проекта, так как вся кодовая база довольно велика, поэтому я не смогу добавить ее в коды и ящик. Кто-нибудь получал эту ошибку с notifyPlugins раньше при работе с chart.js? Если есть еще код, который вам нужно увидеть, дайте мне знать.

ошибка консоли:

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 (строка 6171):

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

ChartMetrics (строка 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();
        }

Строка 454 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 (стройное действие)

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}

На первый взгляд кажется, что это не вызвано плагином. Кажется, в ChartMetrics.svelte в строке 454 есть вызов destroy диаграммы, который делает thisundefined внутри функции, что-то вроде chart.destroy.call() вместо chart.destroy()

kikon 27.05.2023 06:28

Да, это на картинке chartjserror, которую я связал. Будет ли это проблемой с chartjs?

ou9hwq93weiq 29.05.2023 01:38

Нет, я думаю, причина в том, как код из компонента svelte ChartMetrics вызывает destroy. Не могли бы вы опубликовать, что там около строки 454?

kikon 29.05.2023 03:34

Конечно, я отредактировал свой вопрос, чтобы поместить полный код, где вызывается уничтожение.

ou9hwq93weiq 29.05.2023 04:45

Теперь у нас есть проблема узнать, как называется d, чтобы this было undefined. Это ваш код или из импортированной библиотеки (в гугле ничего не нашел)? Вы можете добавить функционирующую/упрощенную версию вашего кода для полной отладки в stackblitz или codeandbox. Кроме того, в качестве дружеского примечания, для будущих сообщений на SO, люди здесь, как правило, неприятно реагируют на код, помещенный в изображения, об этом также есть метассылка.

kikon 29.05.2023 05:10

Да, это компонент моего кода. Я отредактирую ответ как можно большим количеством кода и заменю изображения кодом.

ou9hwq93weiq 29.05.2023 07:49

Я обновил вопрос, моя кодовая база слишком велика, чтобы поместить ее в рабочую среду codeandbox, поэтому я добавил к вопросу. Если есть какой-либо другой код, который вы хотели бы увидеть, дайте мне знать, все еще не продвинулся вообще в отношении этой ошибки.

ou9hwq93weiq 29.05.2023 08:37

Я расскажу вам, как бы я поступил, если бы мог отладить код: я бы поставил console.info(this) в d() непосредственно перед destroy и попытался увидеть, какое свойство this является экземпляром диаграммы (или, может быть, this является подклассом Chart, или, возможно, есть другой способ получить экземпляр диаграммы) и изменить destroy() на destroy.call(chartInstance). Экземпляр диаграммы — это объект, обладающий такими свойствами, как canvas, chartArea, boxes

kikon 29.05.2023 08:52

Таким образом, функция d() на самом деле не является частью моего ChartMetrics.svelte, то, что я вижу в ChartMetrics.svelte в инспекторе браузера, сильно отличается от того, что есть в моей IDE. Полный файл ChartMetrics.svelte из моей IDE находится здесь: stackblitz.com/edit/… а это версия инспектора браузера: stackblitz.com/edit/…

ou9hwq93weiq 29.05.2023 09:17
Зод: сила проверки и преобразования данных
Зод: сила проверки и преобразования данных
Сегодня я хочу познакомить вас с библиотекой Zod и раскрыть некоторые ее особенности, например, возможности валидации и трансформации данных, а также...
Как заставить Remix работать с Mantine и Cloudflare Pages/Workers
Как заставить Remix работать с Mantine и Cloudflare Pages/Workers
Мне нравится библиотека Mantine Component , но заставить ее работать без проблем с Remix бывает непросто.
Угловой продивер
Угловой продивер
Оригинал этой статьи на турецком языке. ChatGPT используется только для перевода на английский язык.
TypeScript против JavaScript
TypeScript против JavaScript
TypeScript vs JavaScript - в чем различия и какой из них выбрать?
Синхронизация localStorage в масштабах всего приложения с помощью пользовательского реактивного хука useLocalStorage
Синхронизация localStorage в масштабах всего приложения с помощью пользовательского реактивного хука useLocalStorage
Не все нужно хранить на стороне сервера. Иногда все, что вам нужно, это постоянное хранилище на стороне клиента для хранения уникальных для клиента...
Что такое ленивая загрузка в Angular и как ее применять
Что такое ленивая загрузка в Angular и как ее применять
Ленивая загрузка - это техника, используемая в Angular для повышения производительности приложения путем загрузки модулей только тогда, когда они...
1
9
57
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Вы можете попробовать этот патч решения: в add-chart.ts вместо

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

использовать

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

Более стройный идиоматический способ мог бы быть

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

но для этого, вероятно, потребуется некоторая акробатика машинописного текста, чтобы пройти проверку типов.

@ ou9hwq93weiq, пожалуйста, дайте мне знать, что произойдет, если вы сделаете первое изменение; мы здесь на пути проб и ошибок

kikon 29.05.2023 10:41

Эй, извините, я только что вернулся к компьютеру. Я только что попробовал ваше решение, и оно работает! Не могли бы вы объяснить, что происходит с svelte-идиоматическим образом? Вы перезаписываете функцию уничтожения для файла ChartMetrics и заменяете ее функцией уничтожения диаграмм?

ou9hwq93weiq 30.05.2023 01:39

Просто чтобы уточнить, оба метода работают, но просто хотите понять, что происходит, когда вы говорите «chart.destroy = chart.destroy.bind(chart)»?

ou9hwq93weiq 30.05.2023 01:41

Таким образом, проблема заключалась в том, что метод уничтожения по умолчанию, созданный svelte (в вашем сгенерированном файле в строке 381), просто взял метод destroy из экземпляра диаграммы и вызвал его с undefined контекстом (this), см. исходный код.

kikon 30.05.2023 01:52

Первый метод использует bind, поэтому функция destroy всегда связана с экземпляром диаграммы, независимо от того, как она вызывается, this будет этим экземпляром.

kikon 30.05.2023 01:52

Второй метод — предоставить явное destroy для svelte вместо стандартного. По сути, это одно и то же, svelte будет вызывать этот метод, но как бы он ни назывался, он заботится о том, чтобы вызов destroy диаграммы был правильным, то есть chart.destroy(), а не просто destroy.

kikon 30.05.2023 02:02

Я сказал, что мне это кажется более идиоматичным, так как я видел во многих примерах, что объект, возвращаемый функцией инициализации в сценарии use: (как в случае с вашим кодом), имеет только update и destroy, остальные созданный экземпляр (экземпляр chart.js в вашем коде) работает сам по себе, но не раскрывается (вы можете изучить его полностью, если он работает для вас). Посмотрите, например, на первый пример в вопросе из этой темы SO.

kikon 30.05.2023 02:03

Я вижу аналогию с моим использованием chart.js в среде vanilla html+js — в большинстве случаев мне не нужно объявлять переменную const chart = new Chart(...., достаточно просто построить экземпляр диаграммы new Chart(...., важно предоставить правильные данные инициализации, тогда он будет работать сам по себе, ссылка на него бесполезна. Что ж, в вашем случае вам нужен метод destroy (я не знаю, нужен ли на самом деле update), поэтому, возможно, ваша функция addChart может возвращать только destroy, то есть без части ...chart во второй версии

kikon 30.05.2023 02:22

Другие вопросы по теме

Angular - холст не перерисовывает фигуры при изменении его размера
При рисовании контуров букв с острыми углами появляются странные артефакты. Как их избежать?
Как проверить, пересекается ли линия с кругом в JavaScript?
Как повторно визуализировать функциональный компонент React в useEffect?
Как получить значения цвета пикселя из холста WebGL в Javascript
Как я могу предотвратить потерю качества и затемнение при объединении двух холстов, которые загружают PDF-файлы в JavaScript?
Проблема в реализации алгоритма медианного разреза с использованием Javascript
Почему мои изображения на холсте не загружаются/рендерятся не в правильной последовательности или не так, как ожидалось с помощью image.onload?
Преобразование изображения html5 <canvas> в растровое изображение, которое можно использовать в drawImage
Добавление гравитации к физике бильярда в анимации холста JS