嵌套选择未更新

时间:2019-01-07 22:36:41

标签: javascript d3.js

我要使用d3js显示一组数据分组。嵌套选择似乎确实看到了更新。如何获得内部分组的更新?

我创建了thishttp://jsfiddle.net/f0m574wp/17/)小提琴。

我有一些嵌套数据,我的应用程序可以异步获取这些嵌套数据,并在数据传入时进行构造和显示。

dat = [{
  key: 'asdf',
  values:[{
      key:'1',
      value:'hello'
    },{
      key:'2',
      value:''
    }]
  },{
  key:'xyz',
  values:[{
    key:'1',
    value:'foo'
  },{
    key:'3',
    value:'bar'
  }]
}];

随着数据对象的更新,我想重新运行显示功能以更新可视化效果。

function display(mydata){
  //let x = mydata;
  let x = JSON.parse(JSON.stringify(mydata));
  console.log('x', x);
  let root = d3.select('div');

  // create the outer groupings
  let outer = root.selectAll('div.outer').data(x, d=>d.key);
  let outerEnter = outer.enter()
    .append('div')
    .classed('outer', true)
    .attr('id', d=>d.key);

  // add a heading and body to contain data
  outerEnter.append('div').classed('heading', true).append('h1');
  outerEnter.append('div').classed('body', true);
  outer.exit().remove();

  // get prevoius and existing outer groups
  let outerall = outerEnter.merge(outer);
  let headings = outerall.selectAll('div.heading');
  let bodies = outerEnter.selectAll('div.body')
  headings.selectAll('h1').text(d=>d.key); // update outer group heading

  //lets work with the inner arrays of data
  let innerGroup = bodies.selectAll('div.mid')
    .data(d=>{ console.log(d); return d.values}, k=>k.key);

  let innerGpEnter = innerGroup.enter()
    .append('div')
    .classed('mid', true);

  let innerGpAll = innerGpEnter.merge(innerGroup);

  // inner heading
  let inner = innerGpAll.selectAll('h4.inner')
    //.call(function(sel){console.log(sel.nodes());})
    .data(d=>{ console.log(d); return [d];}, k=>k.key);
  let innerEnter = inner.enter()
    .append('h4')
    .classed('inner', true);
  innerEnter.merge(inner).text(k=>{console.log(k); return k.key});

  // inner detail
  let p = innerGpAll.selectAll('p.inner').data(d=>[d], k=>k.key);
  let pEnter = p.enter().append('p').classed('inner', true);
  pEnter.merge(p).text(v=>v.value);
}

问题似乎出在内部数据绑定上:

//lets work with the inner arrays of data
let innerGroup = bodies.selectAll('div.mid')
  .data(d=>{ console.log(d); return d.values}, k=>k.key);

随着数据的变化,似乎没有更新内部绑定。

1 个答案:

答案 0 :(得分:3)

这里的主要问题是使用两个链接的selectAll。你有...

let bodies = outerEnter.selectAll('div.body');

...随后是...

let innerGroup = bodies.selectAll('div.mid').etc...

,这给了我们:

let innerGroup = outerEnter.selectAll('div.body').selectAll('div.mid').etc...

首先,您应该选择outerall,而不是outerEnter,因为outerall是更新选择。但是这里的主要问题是链接的selectAll:第一个之后没有数据绑定,第二个之后也没有数据绑定。因此,数据不会传送到内部选择中。

请记住,与select不同,selectAll不会传播数据,这一点很重要。看看我制作的这张桌子:

+-------------------+----------------------------------+----------------------------+
| Method            |            select()              |         selectAll()        |
+-------------------+----------------------------------+----------------------------+
| Selection         | selects the first element        | selects all elements that  |
|                   | that matches the selector string | match the selector string  |
+-------------------+----------------------------------+----------------------------+
| Grouping          | Does not affect grouping         | Affects grouping           |
+-------------------+----------------------------------+----------------------------+
| Data propagation  | Propagates data                  | Doesn't propagate data     |
+-------------------+----------------------------------+----------------------------+

请注意传播数据不会传播数据

解决方案

让我们先创建一个selectAll,然后再使用data方法:

let innerGroup = outerall.selectAll('div.mid').etc...

这是您所做的更改的代码:

dat = [{
  key: 'asdf',
  values: [{
    key: '1',
    value: 'hello'
  }, {
    key: '2',
    value: ''
  }]
}, {
  key: 'xyz',
  values: [{
    key: '1',
    value: 'foo'
  }, {
    key: '3',
    value: 'bar'
  }]
}];

function display(mydata) {
  //let x = mydata;
  let x = JSON.parse(JSON.stringify(mydata));
  console.log('x', x);
  let root = d3.select('div');

  // create the outer groupings
  let outer = root.selectAll('div.outer').data(x, d => d.key);
  let outerEnter = outer.enter()
    .append('div')
    .classed('outer', true)
    .attr('id', d => d.key);

  // add a heading and body to contain data
  outerEnter.append('div').classed('heading', true).append('h1');
  outerEnter.append('div').classed('body', true);
  outer.exit().remove();

  // get prevoius and existing outer groups
  let outerall = outerEnter.merge(outer);
  let headings = outerall.selectAll('div.heading');
  headings.selectAll('h1').text(d => d.key); // update outer group heading

  //lets work with the inner arrays of data
  let innerGroup = outerall.selectAll('div.mid')
    .data(d => {
      console.log(d);
      return d.values
    }, k => k.key);

  let innerGpEnter = innerGroup.enter()
    .append('div')
    .classed('mid', true);

  let innerGpAll = innerGpEnter.merge(innerGroup);

  // inner heading
  let inner = innerGpAll.selectAll('h4.inner')
    //.call(function(sel){console.log(sel.nodes());})
    .data(d => {
      console.log(d);
      return [d];
    }, k => k.key);
  inner.exit().remove();
  let innerEnter = inner.enter()
    .append('h4')
    .classed('inner', true);
  innerEnter.merge(inner).text(k => {
    console.log(k);
    return k.key
  });

  // inner detail
  let p = innerGpAll.selectAll('p.inner').data(d => [d], k => k.key);
  p.exit().remove();
  let pEnter = p.enter().append('p').classed('inner', true);
  pEnter.merge(p).text(v => v.value);
}

display(dat);
setTimeout(function() {
  console.log('Send New data');
  //dat[0].key = 'abc';
  dat[0].values[1].value = 'good bye';
  dat[1].values.push({
    key: 2,
    value: 'baz'
  });
  display(dat);
}, 3000)
* {
  margin: 2px;
  padding: 2px;
  border: 0;
}

body {
  background: #ffd;
}

.outer {
  border: 1px solid green;
  width:
}

.mid {
  border: 1px solid red;
}

.inner {
  border: 1px solid blue;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>
<div>
</div>

解决方案2

根据your comment,您需要保留该HTML结构。在这种情况下,请使用select

let bodies = outerall.select('div.body');

您可以在此处安全地使用select,因为您只需将一个 <div>附加到类body

这是新的代码段:

dat = [{
  key: 'asdf',
  values: [{
    key: '1',
    value: 'hello'
  }, {
    key: '2',
    value: ''
  }]
}, {
  key: 'xyz',
  values: [{
    key: '1',
    value: 'foo'
  }, {
    key: '3',
    value: 'bar'
  }]
}];

function display(mydata) {
  //let x = mydata;
  let x = JSON.parse(JSON.stringify(mydata));
  console.log('x', x);
  let root = d3.select('div');

  // create the outer groupings
  let outer = root.selectAll('div.outer').data(x, d => d.key);
  let outerEnter = outer.enter()
    .append('div')
    .classed('outer', true)
    .attr('id', d => d.key);

  // add a heading and body to contain data
  outerEnter.append('div').classed('heading', true).append('h1');
  outerEnter.append('div').classed('body', true);
  outer.exit().remove();

  // get prevoius and existing outer groups
  let outerall = outerEnter.merge(outer);
  let headings = outerall.selectAll('div.heading');
  let bodies = outerall.select('div.body');
  headings.selectAll('h1').text(d => d.key); // update outer group heading

  //lets work with the inner arrays of data
  let innerGroup = bodies.selectAll('div.mid')
    .data(d => {
      console.log(d);
      return d.values
    }, k => k.key);

  let innerGpEnter = innerGroup.enter()
    .append('div')
    .classed('mid', true);

  let innerGpAll = innerGpEnter.merge(innerGroup);

  // inner heading
  let inner = innerGpAll.selectAll('h4.inner')
    //.call(function(sel){console.log(sel.nodes());})
    .data(d => {
      console.log(d);
      return [d];
    }, k => k.key);
  inner.exit().remove();
  let innerEnter = inner.enter()
    .append('h4')
    .classed('inner', true);
  innerEnter.merge(inner).text(k => {
    console.log(k);
    return k.key
  });

  // inner detail
  let p = innerGpAll.selectAll('p.inner').data(d => [d], k => k.key);
  p.exit().remove();
  let pEnter = p.enter().append('p').classed('inner', true);
  pEnter.merge(p).text(v => v.value);
}

display(dat);
setTimeout(function() {
  console.log('Send New data');
  //dat[0].key = 'abc';
  dat[0].values[1].value = 'good bye';
  dat[1].values.push({
    key: 2,
    value: 'baz'
  });
  display(dat);
}, 3000)
* {
  margin: 2px;
  padding: 2px;
  border: 0;
}

body {
  background: #ffd;
}

.outer {
  border: 1px solid green;
  width:
}

.mid {
  border: 1px solid red;
}

.inner {
  border: 1px solid blue;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>
<div>
</div>