Reputation: 190
I have the following code which is simple enough to update a nested data model, but the text(null)
section seems a bit weird.
I'm not sure if I'm doing it right or there is better way to do it:
var data = [
{key: 1, values:[1, 2, 3]},
{key: 2, values:[3, 4, 5]},
]
function update(data) {
var table = d3.select('#gogo')
var tr = table.selectAll('tr')
.data(data)
.text(null)
var rowEnter = tr.enter().append('tr')
var td = tr.merge(rowEnter).selectAll("td")
.data(d=>d.values)
.text(d=>d)
cellEnter = td.enter().append("td")
cellEnter.append("span")
.text(d=>d)
}
update(data)
ref()
function ref() {
setInterval(()=>{
data.map(item=> {
for (var i=0; i<3; i++) {
item.values[i] = Math.floor(Math.random() * 100) + 1
}
})
console.log(data)
update(data)
}, 1000)
}
basically it just renders the following html code:
<table id="gogo"><tr><td><span>1</span></td><td><span>2</span></td><td><span>3</span></td></tr><tr><td><span>3</span></td><td><span>4</span></td><td><span>5</span></td></tr></table>
Please somebody gives me advise of it.
Upvotes: 2
Views: 396
Reputation: 102218
You are correct, there is a better way. Right now your code is confuse, it has some things that make no sense (like that text(null)
) and, the most important, it cannot update the table for any different number of rows and cells... and that's the most important feature of an update selection.
This is my suggestion for your update function:
function update(data) {
var table = d3.select('#gogo');
var tr = table.selectAll('tr')
.data(data);
var rowExit = tr.exit().remove();
var rowEnter = tr.enter().append('tr');
tr = rowEnter.merge(tr);
var td = tr.selectAll("td")
.data(d => d.values);
var cellExit = td.exit().remove();
var cellEnter = td.enter().append("td").append("span");
td = cellEnter.merge(td)
.text(d => d)
}
Let's see it line by line.
First, the table selection:
var table = d3.select('#gogo');
Based on that selection, we'll create the update selection for the rows:
var tr = table.selectAll('tr')
.data(data);
Then, based on that update selection, we create the enter selection for the rows:
var rowEnter = tr.enter().append('tr');
After that, we merge the enter and update selection for the rows:
tr = rowEnter.merge(tr);
This allows us to have any number of rows. Of course, to have a real update function, we have to set the exit selection as well:
var rowExit = tr.exit().remove();
Then, for the next level, which are the cells, we do pretty much the same thing, an update, an exit and an enter selection with merge
:
var td = tr.selectAll("td")
.data(d => d.values);
var cellExit = td.exit().remove();
var cellEnter = td.enter().append("td").append("span");
td = cellEnter.merge(td)
.text(d => d);
Here is the demo with your ref
function:
var data = [{
key: 1,
values: [1, 2, 3]
},
{
key: 2,
values: [3, 4, 5]
},
]
function update(data) {
var table = d3.select('#gogo');
var tr = table.selectAll('tr')
.data(data);
var rowExit = tr.exit().remove();
var rowEnter = tr.enter().append('tr');
tr = rowEnter.merge(tr);
var td = tr.selectAll("td")
.data(d => d.values);
var cellExit = td.exit().remove();
var cellEnter = td.enter().append("td").append("span");
td = cellEnter.merge(td)
.text(d => d)
}
update(data)
ref()
function ref() {
setInterval(() => {
data.map(item => {
for (var i = 0; i < 3; i++) {
item.values[i] = Math.floor(Math.random() * 100) + 1
}
})
update(data)
}, 1000)
}
table, tr, td {
border: 1px solid gray;
}
<script src="https://d3js.org/d3.v5.min.js"></script>
<table id="gogo"></table>
Finally, just to show you that now the update function really updates the table for whatever number of elements, I refactored your ref
function to create data with a random number of rows, from 1 to 5, and a random number of cells, also from 1 to 5:
var data = [{
key: 1,
values: [1, 2, 3]
},
{
key: 2,
values: [3, 4, 5]
},
]
function update(data) {
var table = d3.select('#gogo');
var tr = table.selectAll('tr')
.data(data);
var rowExit = tr.exit().remove();
var rowEnter = tr.enter().append('tr');
tr = rowEnter.merge(tr);
var td = tr.selectAll("td")
.data(d => d.values);
var cellExit = td.exit().remove();
var cellEnter = td.enter().append("td").append("span");
td = cellEnter.merge(td)
.text(d => d)
}
update(data)
ref()
function ref() {
setInterval(() => {
data = d3.range(~~(Math.random() * 5) + 1).map(function(d) {
return {
key: d,
values: d3.range(~~(Math.random() * 5) + 1).map(function(d) {
return ~~(Math.random() * 20)
})
}
})
update(data)
}, 1000)
}
table,
td,
tr {
border: 1px solid gray;
}
<script src="https://d3js.org/d3.v5.min.js"></script>
<table id="gogo"></table>
Upvotes: 1
Reputation: 190
I found the following way seems to more elegant:
var data = [
{key: 1, values:[1, 2, 3]},
{key: 2, values:[3, 4, 5]},
]
function update(data) {
var table = d3.select('#gogo')
var tr = table.selectAll('tr')
.data(data)
var rowEnter = tr.enter().append('tr')
var td = tr.merge(rowEnter).selectAll("td span")
.data(d=>d.values)
.text(d=>d)
cellEnter = td.enter().append("td")
cellEnter.append("span")
.text(d=>d)
}
update(data)
ref()
function ref() {
setInterval(()=>{
data.map(item=> {
for (var i=0; i<3; i++) {
item.values[i] = Math.floor(Math.random() * 100) + 1
}
})
console.log(data)
update(data)
}, 1000)
}
Upvotes: 1