I'm encountering a weird issue that's probably my doing but I've tried everything I can come across.
I have one stacked column chart with eight different data sets. I have a select field with the titles of each data set.
const charts = [
{
"title": "Chart One",
"subtitle": "Subtitle One",
"source": "<p>Source One</p>\n",
"content": "<p>Content One</p>\n",
"series": [
{
"name": "Legend One",
"data": [0,0,0,0,0,0,0,0,0,0,null,0,0,0,0,null,0],
"stack": 0
},
{
"name": "Legend Two",
"data": [1085,1364,2398,2362,3954,6612,6388,8841,8397,6021,null,4962,7407,2825,2143,null,4823],
"stack": 0
}
]
},
{
"title": "Chart Two",
"subtitle": "Subtitle Two",
"source": "<p>Source Two</p>\n",
"content": "<p>Content Two</p>\n",
"series": [
{
"name": "Legend One",
"data": [380,608,469,285,634,1496,712,3059,1821,1049,null,916,2240,612,895,null,1064],
"stack": 0
},
{
"name": "Legend Two",
"data": [705,756,1928,2078,3320,5116,5676,5782,6576,4973,null,4046,5167,2212,1248,null,3759],
"stack": 0
}
]
},
{
"title": "Chart Three",
"subtitle": "Subtitle Three",
"source": "<p>Source Three</p>\n",
"content": "<p>Content Three</p>\n",
"series": [
{
"name": "Legend One",
"data": [648,932,1708,1326,2246,4646,4143,6732,6042,4222,null,3268,5723,1987,1501,null,3322],
"stack": 0
},
{
"name": "Legend Two",
"data": [438,432,690,1037,1708,1966,2244,2110,2355,1799,null,1694,1685,838,642,null,1501],
"stack": 0
}
]
},
{
"title": "Chart Four",
"subtitle": "Subtitle Four",
"source": "<p>Source Four</p>\n",
"content": "<p>Content Four</p>\n",
"series": [
{
"name": "Legend One",
"data": [380,608,469,285,634,1496,712,3118,2498,3567,null,1411,2687,698,1156,null,1479 ],
"stack": 0
},
{
"name": "Legend Two",
"data": [705,756,1928,2078,3320,5116,5676,5724,5899,2455,null,3551,4720,2126,987,null,3344],
"stack": 0
}
]
},
{
"title": "Chart Five",
"subtitle": "Subtitle Five",
"source": "<p>Source Five</p>\n",
"content": "<p>Content Five</p>\n",
"series": [
{
"name": "Legend One",
"data": [561,852,1362,2012,3404,5643,6195,8153,8158,5905,null,4502,6243,2646,1750,null,4305],
"stack": 0
},
{
"name": "Legend Two",
"data": [524,512,1036,350,550,969,193,689,240,117,null,460,1165,178,393,null,518],
"stack": 0
}
]
},
{
"title": "Chart Six",
"subtitle": "Subtitle Six",
"source": "<p>Source Six</p>\n",
"content": "<p>Content Six</p>\n",
"series": [
{
"name": "Legend One",
"data": [120,209,397,655,1115,2584,2944,3322,3075,2266,null,1751,2606,1032,740,null,1704],
"stack": 0
},
{
"name": "Legend Two",
"data": [965,1155,2000,1708,2840,4027,3443,5519,5323,3755,null,3211,4802,1793,1403,null,3118],
"stack": 0
}
]
},
{
"title": "Chart Seven",
"subtitle": "Subtitle Seven",
"source": "<p>Source Seven</p>\n",
"content": "<p>Content Seven</p>\n",
"series": [
{
"name": "Legend One",
"data": [294,494,391,800,1765,4227,5474,7212,7705,5836,null,3755,4640,2070,1463,null,3490],
"stack": 0
},
{
"name": "Legend Two",
"data": [791,870,2007,1563,2189,2385,914,1630,692,186,null,1207,2768,755,680,null,1333],
"stack": 0
}
]
},
{
"title": "Chart Eight",
"subtitle": "Subtitle Eight",
"source": "<p>Source Eight</p>\n",
"content": "<p>Content Eight</p>\n",
"series": [
{
"name": "Legend One",
"data": [486,741,1160,1825,3105,5205,6012,7553,7723,5780,null,4259,5754,2424,1596,null,4035],
"stack": 0
},
{
"name": "Legend Two",
"data": [599,623,1237,537,849,1407,376,1288,674,242,null,703,1654,401,547,null,787],
"stack": 0
}
]
}
]
When a user selects the chart they want to view, I then grab the data needed charts[selectedIndex].series and pass that to:
chartObj.update({
series: charts[selectedIndex].series
}, false );
chartObj.redraw();
This overall works and the chart is updated correctly, however, chart[0].series
gets updated with the last selected chart data. The chart array is getting modified for some odd reason. It only modifies the first chart in the array. For the life of me, I can't figure out why this is occurring. Any insight would be greatly appreciated.
I've also tried a for loop going through the chartObj series and matching it to the desired charts array item, and using .setData
with the 'data' but that had it's own issues. This did not modify the original array, however the chart would no longer update. I tried the same loop with .update as well.
Codepen with the error: https://codepen.io/rossberenson/pen/mdrWgPe
I dug a little deeper and found that the chart.update()
call was switching out the nested series
array but wasn't updating the entire chart object, more than that even cloning the series
array using map(o => ({...o}))
doesn't clone the nested data
arrays so they are still prone to mutation. (you could use map(o => ({...o, data: [...o.data]}))
but it's fragile if your series/data structure changes).
So... here is a quick snippet that stores the datasets in their own array and sets the Highchart.series
option to a separate active
object.
The active
object is initially assigned a cloned version of the first chart object using JSON
for deep cloning – see this question for deeper discussion of its shortcomings and better options What is the most efficient way to deep clone an object in JavaScript?
When the select change
event fires, the chart object at the newly selected index is cloned using the same method and assigned to the active
object.
Finally, chart.update()
is called pointing to the same active.series
as when the highchart class was instantiated.
const cloneSample = (sample) => {
// Crude JSON deep clone – see referenced question for discussion
return JSON.parse(JSON.stringify(sample));
}
let active = cloneSample(samples[0]);
const highchart = Highcharts.chart('container', {
chart: { type: 'bar' }, title: { text: active.title },
xAxis: { categories: ['Apples', 'Oranges', 'Pears', 'Grapes', 'Bananas'] },
yAxis: { min: 0, title: { text: 'Total fruit consumption' } }, legend: { reversed: false },
plotOptions: { series: { stacking: 'normal' } },
series: active.series
});
const select = document.querySelector('.select');
select.addEventListener('change', () => {
let
selected = select.value,
newSample = cloneSample(samples[selected]);
active = newSample;
highchart.update({
title: { text: active.title },
series: active.series,
});
});
<script src="https://code.highcharts.com/highcharts.js"></script>
<select class="select">
<option value="0">Sample One</option>
<option value="1">Sample Two</option>
<option value="2">Sample Three</option>
</select>
<div id="container"></div>
<script>
const samples = [
{
"title": "Sample One",
"series": [
{
name: 'John',
data: [5, 3, 4, 7, 2]
}, {
name: 'Jane',
data: [2, 2, 3, 2, 1]
}, {
name: 'Tara',
data: [3, 4, 4, 2, 5]
}
]
},
{
"title": "Sample Two",
"series": [
{
name: 'Donna',
data: [2, 1, 4, 6, 2]
}, {
name: 'Mark',
data: [1, 4, 3, 4, 1]
}, {
name: 'Tim',
data: [6, 4, 9, 3, 5]
}
]
},
{
"title": "Sample Three",
"series": [
{
name: 'Mara',
data: [3, 3, 1, 7, 2]
}, {
name: 'Tom',
data: [0, 1, 2, 4, 1]
}, {
name: 'Lara',
data: [3, 8, 5, 2, 5]
}
]
}
]
</script>