Updating HTML table in d3.js using reusable chart

I have this reusable template to create a table based on http://bl.ocks.org/3687826 and I have two questions.

This is the function:

d3.table = function(config) {
var columns = [];

var tbl = function(selection) {
if (columns.length == 0) columns = d3.keys(selection.data()[0][0]);
console.log(columns)    

// Creating the table
var table = selection.append("table");
var thead = table.append("thead");
var tbody = table.append("tbody");

// appending the header row
var th = thead.selectAll("th")
        .data(columns)

th.enter().append("th");
    th.text(function(d) { return d });
th.exit().remove()

// creating a row for each object in the data
var rows = tbody.selectAll('tr')
    .data(function(d) { return d; })

rows.enter().append("tr");
rows.attr('data-row',function(d,i){return i});
rows.exit().remove();   

// creating a cell for each column in the rows
var cells = rows.selectAll("td")
        .data(function(row) {
    return columns.map(function(key) {
                return {key:key, value:row[key]};
    });
        })

cells.enter().append("td");
cells.text(function(d) { return d.value; })
    .attr('data-col',function(d,i){return i})
    .attr('data-key',function(d,i){return d.key});
cells.exit().remove();

return tbl;
};

tbl.columns = function(_) {
if (!arguments.length) return columns;
columns = _;
return this;
};

return tbl;
};

      

This table can be called like this:

/// new table
var t = d3.table();

/// loading data
d3.csv('reusable.csv', function(error,data) {
    d3.select("body")
    .datum(data.filter(function(d){return d.price<850})) /// filter on lines
    .call(t)
});

      

where the reusable.csv file looks something like this:

date,price
Jan 2000,1394.46
Feb 2000,1366.42
Mar 2000,1498.58
Apr 2000,1452.43
May 2000,1420.6
Jun 2000,1454.6
Jul 2000,1430.83
Aug 2000,1517.68
Sep 2000,1436.51

      

and the number of columns can be updated with

t.columns(["price"]); 
d3.select("body").call(t);

      

The problem is that the update creates another table with thead and tbody as the table creation is inside the function.

How can I say " create a table only once and then update"?

Another question: how can I filter the rows using a method inside a function?

+3


source to share


1 answer


The problem is with these three lines of code:

// Creating the table
var table = selection.append("table");
var thead = table.append("thead");
var tbody = table.append("tbody");

      

which always adds new table elements, thead and tbody to your document. Here you can only conditionally do this when those elements don't exist yet (the example you cite creates its div.header element in a similar way):

selection.selectAll('table').data([0]).enter().append('table');
var table = selection.select('table');

table.selectAll('thead').data([0]).enter().append('thead');
var thead = table.select('thead');

table.selectAll('tbody').data([0]).enter().append('tbody');
var tbody = table.select('tbody');

      



SelectAll () data. ([0]). enter (). Example: append () conditionally creates one element if not found. The above example uses data ([true]), but any array with one element will work.

To filter nested data from your function, change your call to data () and pass the filtered subset of the selection data like this:

var rows = tbody.selectAll('tr').data(tbody.data()[0].filter(function(d) { 
    return d.price > 1400; 
}));

      

Good luck!

+4


source







All Articles