加载数据一次,但最好有一个全局变量" var data;"

时间:2017-03-04 16:36:13

标签: javascript d3.js

我是js的新手并且已经知道有潜伏在阴影中的怪物"当涉及变量的使用及其各自的范围以及隐藏数据的方式时。

我使用d3.js并从csv加载数据但我只想进行一次GET调用,因为更复杂的可视化可能涉及10个csv文件,其中一些文件包含多达10,000行数据 - 因此我&#39 ; ve在我的脚本顶部添加了一个变量" var data;"在初始渲染时填充,然后该变量用于来自用户的后续交互。

我的方法安全吗?如果有更好的模式,我应该使用它是什么?

BarData.csv如下所示:

Fruit,dt,amount
Apple,12/28/2016,-1256
Apple,12/29/2016,-500
Apple,12/30/2016,3694
Apple,12/31/2016,5586
Apple,1/1/2017,4558
Apple,1/2/2017,6696
Apple,1/3/2017,7757
Apple,1/4/2017,8528
Apple,1/5/2017,5543
Apple,1/6/2017,3363
Apple,1/7/2017,5464
Pear,12/25/2017,250
Pear,12/26/2017,669
Pear,12/27/2017,441
Pear,12/28/2017,159
Pear,12/29/2017,357
Pear,12/30/2017,775
Pear,12/31/2017,669

代码全部在一个html文件中,如下所示:

<!DOCTYPE html>
<html>

<head>
  <meta http-equiv="Content-type" content="text/html; charset=utf-8">
  <title>BAR SINGLE FUNCTION</title>
  <script src="http://d3js.org/d3.v3.js"></script>
  <style type="text/css">
    #radioDiv {
      top: 45px;
      font-family: verdana;
      font-size: 8px;
      width: 455px;
    }

    #TOPbarChart {
      position: absolute;
      top: 50px;
      left: 30px;
      width: 750px;
      height: 195px;
    }

    .axis--y path,
    .axis--x path {
      display: none;
    }

    .axis--x line,
    .axis--y line {
      stroke: black;
      fill: none;
      stroke-width: 2px
    }

    .yAxis text,
    .xAxis text {
      font: 7pt Verdana;
      stroke: none;
      fill: black;
    }

    .title,
    .titleX {
      font-family: Verdana;
      font-size: 10px;
    }
  </style>
</head>

<body>
  <div id="radioDiv">
    <label>
      <input id="radioFrt" type="radio" name="frt" value="Apple" class="radioB" checked> APPLE
    </label>
    <label>
      <input type="radio" name="frt" value="Pear" class="radioB"> PEAR
    </label>
  </div>
  <div id="TOPbarChart"></div>
  <script type="text/javascript">
    var currentFruit = "Apple";
    var currentColr = "#00a5b6";

    var barDataCSV_Dly = "BarData.csv";

    var data;
    //
    //
    // radio button
    document.getElementById("radioFrt").checked = true;
    d3.selectAll('input[name="frt"]').on("change", function change() {
      currentFruit = this.value;
      //load();
      TOPbarChart(currentFruit, currentColr);
    });

    //FORMATS 
    var parseDate = d3.time.format("%m/%d/%Y").parse;



    // 
    // BASIC SIZING
    // 
    function barChartBasics() {
      var margin = {
          top: 25,
          right: 35,
          bottom: 25,
          left: 70
        },
        width = 550 - margin.left - margin.right,
        height = 155 - margin.top - margin.bottom,
        colorBar = d3.scale.category20(),
        barPaddingFine = 1,
        barPaddingThick = 2;
      return {
        margin: margin,
        width: width,
        height: height,
        colorBar: colorBar,
        barPaddingFine: barPaddingFine,
        barPaddingThick: barPaddingThick
      };
    }


    // create svg element
    var basics = barChartBasics();
    var svg = d3.select("#TOPbarChart")
      .append("svg")
      .attr({
        "width": basics.width + basics.margin.left + basics.margin.right,
        "height": basics.height + basics.margin.top + basics.margin.bottom,
        id: "svgTOPbarChart"
      });

    // create svg  group
    var plot = svg
      .append("g")
      .attr({
        "transform": "translate(" + basics.margin.left + "," + basics.margin.top + ")",
        id: "svgPlotTOPbarChart"
      });

    var axisPadding = 2;
    var leftAxisGroup = svg
      .append('g')
      .attr({
        transform: 'translate(' + (basics.margin.left - axisPadding) + ',' + (basics.margin.top) + ')',
        'class': "yAxis axis--y",
        id: "yAxisGTOPbarChart"
      });

    var bottomAxisGroup = svg
      .append('g')
      .attr({
        'class': "xAxis axis--x",
        id: "xAxisGTOPbarChart"
      });

    var titleTxt = svg.append("text")
      .attr({
        x: basics.margin.left + 12,
        y: 20,
        'class': "title",
        'text-anchor': "start"
      })

    // create scales with ranges
    var xScale = d3.time.scale().range([0, basics.width]);
    var yScale = d3.scale.linear().range([basics.height, 0]);




   function load(){
     d3.csv(barDataCSV_Dly, function(rows) {
        data = rows;
        TOPbarChart(currentFruit, currentColr)
     })
   }


    function TOPbarChart(
      frt, colorChosen) {

      // get the data
      //d3.csv(barDataCSV_Dly, function(rows) {

        TOPbarData = data.map(function(d) {
          return {
            "Fruit": d.Fruit,
            "dt": parseDate(d.dt),
            "amount": +d.amount
          };
        }).filter(function(row) {
          if (row['Fruit'] == frt) {
            return true;
          }
        });


        // create domains for the scales
        xScale.domain(d3.extent(TOPbarData, function(d) {
          return d.dt;
        }));
        var amounts = TOPbarData.map(function(d) {
          return d.amount;
        });
        var yMax = d3.max(amounts);
        var yMin = d3.min(amounts);
        var yMinFinal = 0;
        if (yMin < 0) {
          yMinFinal = yMin;
        }



        yScale.domain([yMinFinal, yMax]);


        // introduce the bars
        // var plot = d3.select("#svgPlotTOPbarChart")
        var sel = plot.selectAll("rect")
          .data(TOPbarData);

        sel.enter()
          .append("rect")
          .attr({
            x: function(d, i) {
              return xScale(d.dt);
            },
            y: function(d) {
              return yScale(Math.max(0, d.amount));
            },
            width: (basics.width / TOPbarData.length - basics.barPaddingFine),
            height: function(d) {
              return Math.abs(yScale(d.amount) - yScale(0));
            },
            //fill: colorChosen,
            fill: function(d, i) {
              return d.amount < 0 ? "#FF0000" : colorChosen;
            },
            // fill: function(d, i) {
            //            var col = colorChosen
            //              if (d.amount < 0) {
            //                col = "#FF0000";
            //            }
            //            return col;
            //        },
            'class': "bar"
          });

        // this little function will create a small ripple affect during transition
        var dlyRipple = function(d, i) {
          return i * 100;
        };

        sel
          .transition()
          .duration(dlyRipple) //1000
          .attr({
            x: function(d, i) {
              return xScale(d.dt);
            },
            y: function(d) {
              return yScale(Math.max(0, d.amount));
            },
            width: (basics.width / TOPbarData.length - basics.barPaddingFine),
            height: function(d) {
              return Math.abs(yScale(d.amount) - yScale(0));
            },
            //fill: colorChosen
            fill: function(d, i) {
              var col = colorChosen
              if (d.amount < 0) {
                col = "#FF0000";
              }
              return col;
            },
          });

        sel.exit().remove();


        // add/transition y axis - with ticks and tick markers
        var axisY = d3.svg.axis()
          .orient('left')
          .scale(yScale)
          .tickFormat(d3.format("s")) // use abbreviations, e.g. 5M for 5 Million
          .outerTickSize(0);
        leftAxisGroup.transition().duration(1000).call(axisY);

        // add/transition x axis - with ticks and tick markers
        var axisX = d3.svg.axis()
          .orient('bottom')
          .scale(xScale);

        bottomAxisGroup
          .attr({
            transform: 'translate(' + (basics.margin.left + ((basics.width / TOPbarData.length) / 2)) + ',' + (basics.margin.top + basics.height) + ')',
          })
          .transition().duration(1000).call(axisX.ticks(5));


        titleTxt.text("Daily: last " + TOPbarData.length + " days");
        // console.log(TOPbarData.length)

      //});
    }

    //
    //
    //
    //
    load()
    //TOPbarChart(currentFruit, currentColr);
    //
    //
    //
    //
  </script>
</body>

</html>

Plunker的工作示例:

http://plnkr.co/edit/lfWgibRsm6mMzLYSgR2o?p=info

编辑

我试过了Piotr的建议,所以我现在有了这个:

var Module_BarDataDaily = (function() {
    var data;
    d3.csv("myData.csv", function(rows) {
        data = rows;
    });
    return {
        dataX: data
    }
})();

然后在后续功能中我有以下内容:

var x = Module_BarDataDaily.dataX;

TOPbarData = x.map(function(d) {  /<<< Type error x is undefined
    return {
...

在上面标记的行处抛出一个未定义的错误。

2 个答案:

答案 0 :(得分:1)

在变量中缓存记录是非常糟糕的,直到它是私有的,并且在脚本中的任何其他位置都不可见。

您当前的方法在全局命名空间中设置data变量,基本上如果您包含的任何其他插件也将具有名为data的变量,可能会覆盖它的值。 - 这不安全

要隐藏来自世界其他地方的变量,您可能希望将所有代码包含在自调用函数中:

(function() {
  var data = ...; // It's accessible only in that function

  // Your JavaScript code

})();

相关说明

如果要在文件之间共享变量,请在对象(称为模块)中进行,例如:

var Module = (function() {
  var data = ...
  // Your JavaScript code

  return {
     data : data
  }      
})();

// Now it's accessible with Module.data

JavaScript提供内置模块系统,类似于Python。但是,浏览器尚不支持它。

相关说明

PS:将CSS和JavaScript分离到外部文件以避免混乱。

答案 1 :(得分:1)

更新问题后,还有另一个问题asynchronous data

库函数d3.csv(...)与其他计算异步工作,这意味着您无法确定data是否已填充。它可能是立即的,也可能是几秒钟之后。

这就是你获得x is undefined的原因,因为在解析代码的那一刻,d3.csv还没有完成计算。

处理此代码的最简单方法是使用callback函数。 基本上,您的模块返回函数loadData,它接受​​仅在模块知道data已填充时才会调用的函数。

var Module_BarDataDaily = (function() {
    var data;

    return {
        loadData: lazyLoadData
    };

    function lazyLoadData(callback) {
        if (data) {
            callback(data); // data already exists, run function
        } else {
            d3.csv("myData.csv", function(rows) {
                data = rows;
                callback(data); // data is filled, run function
            });
        }
    }
})();

以下是该尝试的使用方法。

Module_BarDataDaily.loadData(function(data) {
    // this function loads only after data is filled
    TopChartData = data.map(...);
});

什么是回调?

  

简单来说,回调函数就像是一个调用的工作者   背面&#34;当他完成任务时,他的经理。

注意:还有许多其他可能更好的尝试来解决ES2015中的Promiseawait等问题,这些问题将随ES2017一起发布。