chartjs / chartjs-plugin-annotation

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

Error from undefined label: "Cannot use 'in' operator to search for 'callout' in undefined" #797

Closed joshkel closed 2 years ago

joshkel commented 2 years ago

Starting with chartjs-plugin-annotation 2.0.0, explicitly setting an annotation's label to undefined (instead of omitting it) results in the following error:

Cannot use 'in' operator to search for 'callout' in undefined
subGetTarget
https://l2i7fk.csb.app/node_modules/chart.js/dist/chunks/helpers.segment.mjs:2720:15
eval
https://l2i7fk.csb.app/node_modules/chart.js/dist/chunks/helpers.segment.mjs:2710:77
Object.set
https://l2i7fk.csb.app/node_modules/chart.js/dist/chunks/helpers.segment.mjs:2561:61
Object.set
https://l2i7fk.csb.app/node_modules/chart.js/dist/chunks/helpers.segment.mjs:2604:19
resolveLabelElementProperties
https://l2i7fk.csb.app/node_modules/chartjs-plugin-annotation/dist/chartjs-plugin-annotation.esm.js:1063:27
LineAnnotation.resolveElementProperties
https://l2i7fk.csb.app/node_modules/chartjs-plugin-annotation/dist/chartjs-plugin-annotation.esm.js:875:29
updateElements
https://l2i7fk.csb.app/node_modules/chartjs-plugin-annotation/dist/chartjs-plugin-annotation.esm.js:1854:32

Demo: https://codesandbox.io/s/chartjs-annotation-undefined-label-l2i7fk

stockiNail commented 2 years ago

@kurkle it sounds an issue in CHART.JS. In the plugin the options.label.callout is correctly a proxy but when it sets the display option, the exception occurs. What do you think?

stockiNail commented 2 years ago

@kurkle @joshkel I think the issue as shown is in helpers.config.js in CHART.JS.

The _getTarget function scans recursively all resolvers and when it lands on the annotation line config, the label is null.

function subGetTarget(resolver, prop, value) {
  const parent = resolver._getTarget(); //   <-----------ERROR HERE
  if (!(prop in parent)) {
    parent[prop] = {};
  }
  const target = parent[prop];
  if (isArray(target) && isObject(value)) {
    // For array of objects, the object is used to store updated values
    return value;
  }
  return target;
}

I have changed that row with

const parent = resolver._getTarget() || {}; 

and this is solving the issue. I think it should be correct (but not sure), because if not defined, I should create an empty object.

stockiNail commented 2 years ago

@joshkel FYI the PR to fix the issue has been approved and merged in CHART.JS project. It will be available with version 4.0.

joshkel commented 2 years ago

Thanks for fixing this, @stockiNail. I'll go ahead and mark this issue as closed.