Reputation: 583
I'm having an issue when dealing with manipulating nested data with D3.js
Data consists of array of objects which represent categories
.
Each category has multiple checkin
s.
I want a section for every category
, and within that, a div for each checkin
. I manipulate data over time and rely on update, enter and exit mechanisms of D3.js to properly output the data to DOM.
What I don't understand:
update()
is invoked third time with data1
again, entries are again appended again despite the key function in data being provided? By my understanding at this point D3 should recognise that this is actually an update and that data with the same key already exist in this section.The second problem is probably related to the first one, but now I'm not sure.
Here's an interactive bl.ocks.org: https://bl.ocks.org/mattleonowicz/fd975a6d914f90c9934464df57e498c9
Same code below:
<html>
<body>
<div id="app" />
<script src="https://d3js.org/d3.v5.min.js"></script>
<script>
const data1 = [
{
category: {
id: 1
},
checkins: [
{
lat: 1,
lon: 1
},
{
lat: 2,
lon: 2
}
// ... more objects like this would normally be here
]
}
// ... more objects like this would normally be here
];
const data2 = [
{
category: {
id: 1
},
checkins: [
{
lat: 3,
lon: 3
},
{
lat: 4,
lon: 4
}
// ... more objects like this would normally be here
]
}
// ... more objects like this would normally be here
];
update(data1);
setTimeout(() => update(data2), 2000);
setTimeout(() => update(data1), 4000);
function update(data) {
const categoriesData = d3.select('#app')
.selectAll('section')
.data(data, d => d.category.id);
// CATEGORIES EXIT
categoriesData.exit()
.remove();
// CATEGORIES UPDATE + ENTER - bind all categories with their checkins
const checkinsData = categoriesData.enter()
.append('section')
.merge(categoriesData)
.selectAll('section')
.data(d => d.checkins, d => `${d.lon},${d.lat}`);
// CHECKINS UPDATE : nothing, because checkins will only come and go
// CHECKINS EXIT
checkinsData.exit()
.remove();
// CHECKINS ENTER
checkinsData.enter()
.append('div')
.html(d => `${d.lon},${d.lat}`);
}
</script>
</body>
</html>
Upvotes: 2
Views: 538
Reputation: 720
You have a typo here:
// CATEGORIES UPDATE + ENTER - bind all categories with their checkins
const checkinsData = categoriesData.enter()
.append('section')
.merge(categoriesData)
-> .selectAll('section')
.data(d => d.checkins, d => `${d.lon},${d.lat}`);
You wanted to write .selectAll('div')
in the second selection.
Upvotes: 3