我要使用d3js显示一组数据分组。嵌套选择似乎确实看到了更新。如何获得内部分组的更新?
我创建了this(http://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);
随着数据的变化,似乎没有更新内部绑定。
答案 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>