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

Time:06-03

A way to create more than one chart in only one .js file without having problems with the names of the variables is encapsulating them in different functions and then calling them in the end of the file as you can see here:

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();

When you're doing this, you'll see that there are a lot of repetitions in the charts. And one of the most important principles in programming is DON'T REPEAT YOURSELF (DRY), mainly if you're working in a team and if you don't want to get mad while maintaining and developing the code.

Below you can see an example of a part of the code that is common to all others charts, goes in the beginning of the file and therefore is reusable to all them.

//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

But then comes the odd part. All charts are receiving data from the same source. So how could we call data only once in a way that we don't need to repeat the promise again and again for each one of them?

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();

To be even clearer and simplier we can focus only in this part to find a solution (this is the essential and common to all charts):

    //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);
    })

I tried cutting d3.json("data.json").then(function(data) { etc. put it in a fuction and call the function afterwards, but this didn't work as we can't reach the internal function result var. It was always undefined. Any clue to this case?

CodePudding user response:

JS is lexically scoped so you can use the data parameter if you define your functions within the scope of the promise.

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();

})
  • Related