简体   繁体   English

通过D3绘制HTML表不会更新现有数据

[英]Drawing an HTML table via D3 doesn't update existing data

I'm using D3 to draw an HTML table, and things work great on enter. 我正在使用D3来绘制HTML表格,并且在输入时效果很好。 When I add a new item to my data collection, it adds the new item to the table correctly. 当我向数据集合添加新项目时,它会正确地将新项目添加到表中。

The problem is whenever I update an existing object (an object in the backgroundJobs collection below) within the collection. 问题是每当我更新集合中的现有对象(下面的backgroundJobs集合中的对象)时。 When I re-run the D3 code to sync up the table, it does not work. 当我重新运行D3代码以同步表时,它不起作用。 Nothing happens. 什么都没发生。

Here's the code: 这是代码:

var visibleColumns = ['Name', 'Start', 'End', 'Status', 'Metadata', 'Errors'];

var table = d3.select('#jobs').append('table');
var thead = table.append('thead');
var tbody = table.append('tbody');

thead.append("tr")
    .selectAll("th")
    .data(visibleColumns)
    .enter()
    .append("th")
    .text(function (column) { return column; });

function tick() {
    var rows = tbody.selectAll("tr")
        .data(backgroundJobs, function(d) {
            return d.name;
        })
        .enter()
        .append("tr");

    var cells = rows.selectAll("td")
        .data(function(row) {
            return [{column: 'Name', value: row.name},
                    {column: 'Start', value: row.startedTimestamp},
                    {column: 'End', value: row.endedTimestamp},
                    {column: 'Status', value: row.isRunning},
                    {column: 'Metadata', value: ''},
                    {column: 'Errors', value: row.errorMsg}];
        })
       .enter()
        .append("td")
        .text(function(d) { return d.value; });
}

setInterval(tick, 500);

Please refer to the cool explanation of the data joins . 参考数据连接的很酷的解释。

When you call 你打电话的时候

tbody.selectAll("tr").data(some-new-data);

You actually get 3 selections: 'enter' (whith the new elements not present in the DOM yet), 'exit' (those present in DOM but not longer present in the data) and 'update' which contains nodes that are already in DOM and still have the data assigned to them via the .data call above. 实际上你得到了3个选择:'enter'(尚未在DOM中出现新元素),'exit'(存在于DOM中但不再存在于数据中的那些元素)和'update',其中包含已经存在于DOM中的节点并且仍然通过上面的.data调用分配给他们的数据。

In general, for 'enter' selection you create new nodes, for 'exit' you need to remove the old ones, and for 'update' you just change attributes - may be with some nice transition effect. 一般来说,对于“输入”选择,您需要创建新节点,“退出”需要删除旧节点,而“更新”只需更改属性 - 可能会有一些很好的过渡效果。 See the updated 'tick' function code. 请参阅更新的“tick”功能代码。

function tick() {
    var rows = tbody.selectAll("tr")
    .data(backgroundJobs, function(d) {
        return d.name;
    });

    rows.enter()
        .append("tr");

    rows.order();

    var cells = rows.selectAll("td")
        .data(function(row) {
            return [{column: 'Name', value: row.name},
                {column: 'Start', value: row.startedTimestamp},
                {column: 'End', value: row.endedTimestamp},
                {column: 'Status', value: row.isRunning},
                {column: 'Metadata', value: ''},
                {column: 'Errors', value: row.errorMsg}];
        });

    cells.enter()
        .append("td");

    cells.text(function(d) { return d.value;});

    cells.exit().remove();

    rows.exit().remove();
}

See the Demo (backgroundJobs is switched on timer between the two hard-coded datasets). 参见演示 (backgroundJobs在两个硬编码数据集之间打开定时器)。

The rows variable will be a selection that will only contains nodes if enter() is not empty. 如果enter()不为空,则rows变量将是一个仅包含节点的选择。 The second time around, if you haven't added any new rows into backgroundJobs , the data bind will update the existing nodes and enter() won't contain any nodes (meaning rows won't contain any nodes). 第二次,如果你没有在backgroundJobs添加任何新行,数据绑定将更新现有节点,而enter()将不包含任何节点(意味着rows不包含任何节点)。

You can get around this by holding a reference to the update selection taking advantage of the fact that nodes appended to the enter selection get added to the update selection behind the scenes: 您可以通过保留对更新选择的引用来利用这一事实来解决这个问题,即附加到输入选择的节点会添加到幕后更新选择中:

var rows = tbody.selectAll("tr")
    .data(backgroundJobs, function(d) {
        return d.name;
    });

rows.enter()
    .append("tr");

Now rows will refer to a selection that contains all previously existing and newly added nodes. 现在,行将引用包含所有先前存在和新添加的节点的选择。

In order to be able to add and delete rows dynamically, this strategy works for me: 为了能够动态添加和删除行,此策略适用于我:

// ROW MANAGEMENT

// select all tr elements
var tr = this.table.select("tbody").selectAll("tr").data(cur_data);

// exit rows
tr.exit().remove();

// enter rows
var trEnter = tr.enter().append("tr");

// CELL MANAGEMENT

trEnter.selectAll("td").data(function(d) { return d3.values(d); }).enter().append("td").text(function(d) { return d; });

// select all td elements
var td = tr.selectAll("td").data(function(d) { return d3.values(d); });

// exit cells
td.exit().remove();

// enter and update/add data to cells
td.enter().append("td");
td.text(function(d) { return d; });

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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