Skip to content

The documentation and type correctness of dataElementType #12169

@antoninkriz

Description

@antoninkriz

Expected behavior

Described behavior

According to the documentation docs/developers/charts.md the chart static defaults = { ... } allow dataElementType to be null or false with the following comment:

defaults: {
    // If set to `false` or `null`, no dataset level element is created.
    // If set to a string, this is the type of element to create for the dataset.
    // For example, a line create needs to create a line element so this is the string 'line'
    datasetElementType: string | null | false,

    // If set to `false` or `null`, no elements are created for each data value.
    // If set to a string, this is the type of element to create for each data value.
    // For example, a line create needs to create a point element so this is the string 'point'
    dataElementType: string | null | false,
}

and the type hints in src/types/index.d.ts allow the dataElementType to be even undefined unspecified:

export interface DatasetControllerChartComponent extends ChartComponent {
  defaults: {
    datasetElementType?: string | null | false;
    dataElementType?: string | null | false;
  };
}

although this might be wrong since DatasetControllerChartComponent is never used again in the whole codebase and just stays as an exported type.

If I do not specify dataElementType or set it to null | false | undefined the code should not crash unexpectedly (and should not allocate any arrays, create new objects, ... - unless parsing I guess?).

Current behavior

class MyController extends DatasetController {
    static id = 'my';
    static defaults = {
        dataElementType: null,
    }
    parse() { }
    draw() { }
}

results in

core.registry.js:178 Uncaught Error: "null" is not a registered element.
    at Registry._get (core.registry.js:178:13)
    at Registry.getElement (core.registry.js:74:17)
    at Chart.buildOrUpdateControllers (core.controller.js:445:37)
    at Chart.update (core.controller.js:495:33)
    at new Chart (core.controller.js:193:12)
    at (index):31:1

The same behavior as with null can be observed with dataElementType set to false (allowed by docs) and eve with dataElementType not being defined at all (kinda allowed by the unused type, disallowed by docs).

This happens in
src/core/core.datasetController.js::DatasetController::_insertElements which does not check the value of dataElementType in any way. This function also allocates the meta.data array even though it should not be needed in this case (especially when parsing disabled - probably?).

I found also something similar in
src/core/core.datasetController.js::DatasetController::constructor but this somehow never gets called, at least in my experiments.

Reproducible sample

https://jsfiddle.net/cb37sr54/2/

Optional extra steps/info to reproduce

Gist GitHub: https://gist.github.com/antoninkriz/2ced01a7e90db8ac2dd417064e6a08a7

JSFiddle: https://jsfiddle.net/cb37sr54/2/

This code will raise the described error.

<canvas id="myChart"></canvas>

<script type="importmap">
{
  "imports": {
    "@kurkle/color": "https://cdn.jsdelivr.net/npm/@kurkle/color@0.4.0/+esm",
    "https://cdnjs.cloudflare.com/ajax/libs/Chart.js/4.5.0/chunks/helpers.dataset.js": "https://cdn.jsdelivr.net/npm/chart.js@4.5.1/dist/chunks/helpers.dataset.js"
  }
}
</script>

<script type="module">
import { Chart } from 'https://cdnjs.cloudflare.com/ajax/libs/Chart.js/4.5.0/chart.js'
import { DatasetController } from 'https://cdnjs.cloudflare.com/ajax/libs/Chart.js/4.5.0/chart.js'
import { BarController, BarElement, CategoryScale, LinearScale } from 'https://cdnjs.cloudflare.com/ajax/libs/Chart.js/4.5.0/chart.js'

class MyController extends DatasetController {
    static id = 'my';
    static defaults = {
        dataElementType: false,
    }
    parse() { }
    draw() { }
}

Chart.register(MyController)
Chart.register(BarController, BarElement, CategoryScale, LinearScale);

new Chart(document.getElementById('myChart'), {
    data: {
        labels: ['a', 'b', 'c', 'd'],
        datasets: [{
            type: 'my',
            data: [1, 2, 3, 4],
        }],
    },
});
</script>

Possible solution

Here I define an element to keep the code from crying and successfully render a completely empty chart.

Anyway since I do not define inRange and events: [] is not set in the config I get error on mouse moves over the chart.

core.interaction.js:174 Uncaught TypeError: element.inRange is not a function
    at evaluationFunc (core.interaction.js:174:29)
    at evaluateInteractionItems (core.interaction.js:83:9)
    at getNearestCartesianItems (core.interaction.js:195:3)
    at getNearestItems (core.interaction.js:216:7)
    at nearest (core.interaction.js:356:14)
    at Chart.getElementsAtEventForMode (core.controller.js:823:14)
    at Chart._getActiveElements (core.controller.js:1260:17)
    at Chart._handleEvent (core.controller.js:1213:25)
    at Chart._eventHandler (core.controller.js:1176:26)
    at listener (core.controller.js:988:12)
<canvas id="myChart"></canvas>

<script type="importmap">
{
  "imports": {
    "@kurkle/color": "https://cdn.jsdelivr.net/npm/@kurkle/color@0.4.0/+esm",
    "https://cdnjs.cloudflare.com/ajax/libs/Chart.js/4.5.0/chunks/helpers.dataset.js": "https://cdn.jsdelivr.net/npm/chart.js@4.5.1/dist/chunks/helpers.dataset.js"
  }
}
</script>

<script type="module">
import { Chart } from 'https://cdnjs.cloudflare.com/ajax/libs/Chart.js/4.5.0/chart.js'
import { DatasetController, Element } from 'https://cdnjs.cloudflare.com/ajax/libs/Chart.js/4.5.0/chart.js'
import { BarController, BarElement, CategoryScale, LinearScale } from 'https://cdnjs.cloudflare.com/ajax/libs/Chart.js/4.5.0/chart.js'

class NoopElement extends Element {
    static id = 'noop'
}

class MyController extends DatasetController {
    static id = 'my';
    static defaults = {
        dataElementType: 'noop',
    }
    parse() { }
    draw() { }
}

Chart.register(NoopElement, MyController)
Chart.register(BarController, BarElement, CategoryScale, LinearScale);

new Chart(document.getElementById('myChart'), {
    data: {
        labels: ['a', 'b', 'c', 'd'],
        datasets: [{
            type: 'my',
            data: [1, 2, 3, 4],
        }],
    },
});
</script>

Context

No response

chart.js version

4.5.0

Browser name and version

Chrome 143, macOS 26, Apple ARM

Link to your project

No response

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions