简体   繁体   中英

How to update child elements when changing data?

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.

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).

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM