如何只使用一个 d3.json().then 承诺许多不同的条形图?

How to use only one d3.json().then promisse to many different barcharts?

一种只在一个 .js 文件中创建多个图表而不会出现变量名称问题的方法是将它们封装在不同的函数中,然后在文件末尾调用它们,如下所示:

function barchart2() {
    //your amazing chart code here
}

function barchart2() {
    //your amazing chart code here
}

function barchart3() {
    //your amazing chart code here
}

//plot charts
barchart1();
barchart2();
barchart3();

执行此操作时,您会发现图表中有很多重复项。 编程中最重要的原则之一是不要重复自己 (DRY),主要是如果您在团队中工作并且不想在维护和开发代码时发疯。

您可以在下面看到所有其他图表通用的部分代码示例,该代码位于文件的开头,因此可供所有图表重复使用。

//set the dimensions and margins of the svg for ALL charts and maps
const MARGIN = {top: 16, right: 0, bottom: 32, left: 64},
    WIDTH  = 640 - MARGIN.left - MARGIN.right,
    HEIGHT = 320 - MARGIN.top - MARGIN.bottom;

//set scales for ALL barcharts
const X = d3.scaleBand()
    .range([0, WIDTH])
    .paddingInner(0.04)
    .paddingOuter(0.02);

const Y = d3.scaleLinear()
    .range([HEIGHT, 0]);

//the above part is common for ALL charts, goes outside the function in the beginning of the file

但是奇怪的部分来了。所有图表都从同一来源接收数据。那么我们如何才能以一种不需要为每个数据一次又一次地重复承诺的方式只调用一次数据呢?

function barchart1() {

    //get data
    d3.json("data.json").then(function(data) { //this part is the same in all 3 charts except for a small part (see comment below)
        //format data
        data.forEach(function(d) {
            d.votes = Number(d.votes);
            d.tt_votes = Number(d.tt_votes);
        });

        const selectedData = [...new Map(data.map(item => [item['code'], item]))
            .values()]
            .sort((a, b) => d3.ascending(a.code, b.code)); //sort list by code

        //plot chart on page first load
        update(data); //calls update function

        let dropdown = d3.select("#select1") //THIS ONLY LINE IS NOT THE SAME IN THE DIFFERENT CHARTS

        //add the options to the dropdown
        dropdown
            .selectAll('myOptions')
            .data(selectedData)
            .enter()
            .append("option")
            .text(function (d) { return d.state_name; }) //text showed in the options
            .attr("value", function (d) { return d.state_code; }) //value of each option
            .property("selected", function(d) { //select state_code === 33 as default
                return d.state_code === 33;
            });

        //calls update function to plot the chart the first time page is load
        update(data);
    })
    
    function update(data){
        //update code goes here
        ...
    }
}

function barchart2() {
    //here the same excepted for let dropdown = d3.select("#select2")
}

function barchart3() {
    //here the same excepted for let dropdown = d3.select("#select3")
}

//plot charts
barchart1();
barchart2();
barchart3();

为了更加清晰和简单,我们可以只关注这部分来找到解决方案(这是所有图表的基本和共同点):

    //get data
    d3.json("data.json").then(function(data) { //this part is the same in all 3 charts except for a small part (see comment below)
        //format data
        data.forEach(function(d) {
            d.votes = Number(d.votes);
            d.tt_votes = Number(d.tt_votes);
        });

        //calls update function to plot the chart the first time page is load
        update(data);
    })

我试过将 d3.json("data.json").then(function(data) { etc. 放在一个函数中,然后调用该函数,但这没有用,因为我们无法访问内部函数结果 var。它总是未定义的。这个案子有什么线索吗?

JS 是词法范围的,因此如果您在 promise 范围内定义函数,则可以使用 data 参数。

d3.json("data.json").then(function(data) {
    /// you can do data prep and make scales here
    function barchart1() {
    // you can refer to data now
    }
    function barchart2() {
    // you can refer to data now
    }

    barchart1();
    barchart2();

})