From 10ffe753d936c7ba059f2b436afed374bb33d252 Mon Sep 17 00:00:00 2001 From: Jukka Kurkela Date: Fri, 6 Nov 2020 15:05:56 +0200 Subject: [PATCH] When data is removed, remove from stacks too (#8013) * When data is removed, remove from stacks too * Remove unreferenced metasets --- src/core/core.controller.js | 17 ++++++++- src/core/core.datasetController.js | 29 +++++++++----- test/specs/core.datasetController.tests.js | 44 ++++++++++++++++++++++ 3 files changed, 80 insertions(+), 10 deletions(-) diff --git a/src/core/core.controller.js b/src/core/core.controller.js index f17aabb3a..6f724a9de 100644 --- a/src/core/core.controller.js +++ b/src/core/core.controller.js @@ -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] = { diff --git a/src/core/core.datasetController.js b/src/core/core.datasetController.js index dd92728f7..83566956d 100644 --- a/src/core/core.datasetController.js +++ b/src/core/core.datasetController.js @@ -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); } diff --git a/test/specs/core.datasetController.tests.js b/test/specs/core.datasetController.tests.js index 4445324f9..836b219bd 100644 --- a/test/specs/core.datasetController.tests.js +++ b/test/specs/core.datasetController.tests.js @@ -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]; -- 2.47.2