Reputation: 3178
I build these elements ...
<svg>
<g class="group">
<text class="label">Hello</text>
</g>
<g class="group">
<text class="label">World</text>
</g>
</svg>
... from following data:
[
{ id: 'a', label: 'Hello' },
{ id: 'b', label: 'World' },
]
Once create them, I change the data and run .data()
again. However texts are not updated.
Codes https://jsfiddle.net/ginpei/r7b65fzt/ :
function render (data, container) {
const group = container.selectAll('.group')
.data(data, d => d.id)
const entered = group.enter().append('g')
.attr({
class: 'group'
})
entered.append('text')
.attr({
class: 'label'
})
.attr({ y: (d, i) => 20 + i * 20})
const label = group.selectAll('.label')
.text(d => d.label)
}
At the second time, it looks data bound to .group
is updated but data bound to .label
is not. I expected that data would be set as well as the first time.
How to update them?
Actually I found it works if I add .data()
for child elements.
const label = container.selectAll('.label')
.data(data, d => d.id) // <--- added
.text(d => d.label)
Is this the correct way to update them?
It would be great to update them by easier way because my actual codes have a lot of descendant elements.
Upvotes: 2
Views: 549
Reputation: 102174
The solution here is quite simple: just change selectAll
to select
.
const label = group.select('.label')
.text(d => d.label);
To understand why, here is a table I've made with the differences between select
and selectAll
:
Method | select() | selectAll() |
---|---|---|
Selection | selects the first element that matches the selector string | selects all elements that match the selector string |
Grouping | Does not affect grouping | Affects grouping |
Data propagation | Propagates data | Doesn't propagate data |
Pay attention to the propagates data versus doesn't propagate data.
And here is your code with that change and a setTimeout
to change the data:
// d3.js 3.x
const container = d3.select('body').append('svg')
function render(data, container) {
const group = container.selectAll('.group')
.data(data, d => d.id)
const entered = group.enter().append('g')
.attr({
class: 'group'
})
entered.append('text')
.attr({
class: 'label'
})
.attr({
y: (d, i) => 20 + i * 20
})
const label = group.select('.label')
.text(d => d.label);
}
// first try
const data1 = [{
id: 'a',
label: 'Hello'
}, {
id: 'b',
label: 'World'
}, ]
render(data1, container)
// update
const data2 = [{
id: 'a',
label: 'Goodbye'
}, {
id: 'b',
label: 'Universe'
}, ]
setTimeout(function() {
render(data2, container)
}, 1000)
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
PS: Your code won't work with v4 (or v5).
Upvotes: 3