]> git.ipfire.org Git - thirdparty/Chart.js.git/commitdiff
When data is removed, remove from stacks too (#8013)
authorJukka Kurkela <jukka.kurkela@gmail.com>
Fri, 6 Nov 2020 13:05:56 +0000 (15:05 +0200)
committerGitHub <noreply@github.com>
Fri, 6 Nov 2020 13:05:56 +0000 (08:05 -0500)
* When data is removed, remove from stacks too
* Remove unreferenced metasets

src/core/core.controller.js
src/core/core.datasetController.js
test/specs/core.datasetController.tests.js

index f17aabb3afc70e720f93499a9cdb7dd61c7c242b..6f724a9def014b9c1966f01b69224e3ea0c45e36 100644 (file)
@@ -362,12 +362,27 @@ class Chart {
                me._sortedMetasets = metasets.slice(0).sort(compare2Level('order', 'index'));
        }
 
+       /**
+        * @private
+        */
+       _removeUnreferencedMetasets() {
+               const me = this;
+               const datasets = me.data.datasets;
+               me._metasets.forEach((meta, index) => {
+                       if (datasets.filter(x => x === meta._dataset).length === 0) {
+                               me._destroyDatasetMeta(index);
+                       }
+               });
+       }
+
        buildOrUpdateControllers() {
                const me = this;
                const newControllers = [];
                const datasets = me.data.datasets;
                let i, ilen;
 
+               me._removeUnreferencedMetasets();
+
                for (i = 0, ilen = datasets.length; i < ilen; i++) {
                        const dataset = datasets[i];
                        let meta = me.getDatasetMeta(i);
@@ -694,7 +709,7 @@ class Chart {
                const me = this;
                const dataset = me.data.datasets[datasetIndex];
                const metasets = me._metasets;
-               let meta = metasets.filter(x => x._dataset === dataset).pop();
+               let meta = metasets.filter(x => x && x._dataset === dataset).pop();
 
                if (!meta) {
                        meta = metasets[datasetIndex] = {
index dd92728f7ee7d6573a456e09f9b309f610fba184..83566956d1e270c7b1e1861157c0233977c8f1c8 100644 (file)
@@ -191,6 +191,13 @@ function createDataContext(parent, index, point, element) {
        });
 }
 
+function clearStacks(meta, items) {
+       items = items || meta._parsed;
+       items.forEach((parsed) => {
+               delete parsed._stacks[meta.vScale.id][meta.index];
+       });
+}
+
 const optionKeys = (optionNames) => isArray(optionNames) ? optionNames : Object.keys(optionNames);
 const optionKey = (key, active) => active ? 'hover' + _capitalize(key) : key;
 const isDirectUpdateMode = (mode) => mode === 'reset' || mode === 'none';
@@ -293,9 +300,13 @@ export default class DatasetController {
         * @private
         */
        _destroy() {
+               const meta = this._cachedMeta;
                if (this._data) {
                        unlistenArrayEvents(this._data, this);
                }
+               if (meta._stacked) {
+                       clearStacks(meta);
+               }
        }
 
        /**
@@ -357,9 +368,7 @@ export default class DatasetController {
                if (meta.stack !== dataset.stack) {
                        stackChanged = true;
                        // remove values from old stack
-                       meta._parsed.forEach((parsed) => {
-                               delete parsed._stacks[meta.vScale.id][meta.index];
-                       });
+                       clearStacks(meta);
                        meta.stack = dataset.stack;
                }
 
@@ -958,15 +967,13 @@ export default class DatasetController {
         */
        _resyncElements() {
                const me = this;
-               const meta = me._cachedMeta;
-               const numMeta = meta.data.length;
+               const numMeta = me._cachedMeta.data.length;
                const numData = me._data.length;
 
                if (numData > numMeta) {
                        me._insertElements(numMeta, numData - numMeta);
                } else if (numData < numMeta) {
-                       meta.data.splice(numData, numMeta - numData);
-                       meta._parsed.splice(numData, numMeta - numData);
+                       me._removeElements(numData, numMeta - numData);
                }
                // Re-parse the old elements (new elements are parsed in _insertElements)
                me.parse(0, Math.min(numData, numMeta));
@@ -1002,10 +1009,14 @@ export default class DatasetController {
         */
        _removeElements(start, count) {
                const me = this;
+               const meta = me._cachedMeta;
                if (me._parsing) {
-                       me._cachedMeta._parsed.splice(start, count);
+                       const removed = meta._parsed.splice(start, count);
+                       if (meta._stacked) {
+                               clearStacks(meta, removed);
+                       }
                }
-               me._cachedMeta.data.splice(start, count);
+               meta.data.splice(start, count);
        }
 
 
index 4445324f9aa9405ac2b920b7486651fc9d33b071..836b219bdf8d72ee0d1105965338c47f83a32807 100644 (file)
@@ -519,6 +519,50 @@ describe('Chart.DatasetController', function() {
                });
        });
 
+       it('should re-synchronize stacks when data is removed', function() {
+               var chart = acquireChart({
+                       type: 'bar',
+                       data: {
+                               labels: ['a', 'b'],
+                               datasets: [{
+                                       data: [1, 10],
+                                       stack: '1'
+                               }, {
+                                       data: [2, 20],
+                                       stack: '2'
+                               }, {
+                                       data: [3, 30],
+                                       stack: '1'
+                               }]
+                       }
+               });
+
+               expect(chart._stacks).toEqual({
+                       'x.y.1.bar': {
+                               0: {0: 1, 2: 3},
+                               1: {0: 10, 2: 30}
+                       },
+                       'x.y.2.bar': {
+                               0: {1: 2},
+                               1: {1: 20}
+                       }
+               });
+
+               chart.data.datasets[2].data = [4];
+               chart.update();
+
+               expect(chart._stacks).toEqual({
+                       'x.y.1.bar': {
+                               0: {0: 1, 2: 4},
+                               1: {0: 10}
+                       },
+                       'x.y.2.bar': {
+                               0: {1: 2},
+                               1: {1: 20}
+                       }
+               });
+       });
+
        it('should cleanup attached properties when the reference changes or when the chart is destroyed', function() {
                var data0 = [0, 1, 2, 3, 4, 5];
                var data1 = [6, 7, 8];