0

I'm attempting to not have to use a global variable called 'data'

In my js file I now have this function:

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

I was then (probably wrongly) under the impression that I can access this data via Module_BarDataDaily.dataX - so in a subsequent function I do the following:

function TOPbarChart(
    grp, meas, colorChosen) {

    TOPbarData = Module_BarDataDaily.dataX.map(function(d) {  //line 900
        return { ...

Console then just gives me an exception on line 900 of the following:

TypeError: Module_BarDataDaily.dataX is undefined

What am I doing wrong & how do I fix it?

whytheq
  • 34,466
  • 65
  • 172
  • 267
  • If you don't want to have a global, why don't you simply use `data` (I mean, `rows`) inside the `d3.csv` callback, as every idiomatic D3 code does? – Gerardo Furtado Mar 06 '17 at 13:48
  • @GerardoFurtado I have one function that implements the Enter-Update-Exit pattern which works but it calls a GET request every time it is interacted with - so I'm hoping to just get the data _once_ - then when the chart is interacted with, any specific filtering is done on the data variable (the small dashboard uses several csv files all containing thousands of rows) – whytheq Mar 06 '17 at 13:54
  • 1
    That being the case, I'm almost sure that you have a [XY problem](http://meta.stackexchange.com/questions/66377/what-is-the-xy-problem) here. – Gerardo Furtado Mar 06 '17 at 13:58
  • +1 @GerardoFurtado, plus, from a quick look `Module_BarDataDaily.dataX is undefined` because `d3.csv("myData.csv", ... ` has not yet filled the data, so dataX can be accessed but the value is null because no assignment has occurred so far. – mkaran Mar 06 '17 at 14:02
  • @mkaran thanks for comment - old classic "I'm new to javascript" ... but I am. I thought this syntax `})();` at the end of my module function meant that it gets executed automatically? – whytheq Mar 06 '17 at 14:04
  • @GerardoFurtado my question has a definite exception I'm just looking to find a way to not have an exception. – whytheq Mar 06 '17 at 14:07
  • 1
    @whytheq Your design is correct, `();` executes the module function, but the catch here is that `d3.csv...` runs asynchronously :), that's why data is filled at a different time than accessed. ([d3.csv](http://stackoverflow.com/questions/19899005/d3-make-the-d3-csv-function-syncronous)) – mkaran Mar 06 '17 at 14:07
  • @mkaran ok - is there a way to workaround around this? what I'm trying to avoid is having `var data` as a global variable – whytheq Mar 06 '17 at 14:19
  • @whytheq take a look at this https://jsfiddle.net/28b5sxv6/ (or this maybe https://jsfiddle.net/fyx5uqwx/) (unfortunately it won't actually load the csv due to a network error but I cannot find a sample csv right now). It is a simple example on how to define a model to hold the data, perform `d3.csv...` once and orchestrate after that. It may not be the best example, since I believe promises etc. would be the way to go with async functions but it may help with what you need. Hope this helps! – mkaran Mar 06 '17 at 14:56
  • @mkaran excellent - I'll give both a go - you could add as answer if you like and I will mark as the answer? – whytheq Mar 06 '17 at 15:15

2 Answers2

1

The issue here is that d3.csv is asynchronous, so data is filled at a different time than accessed.

If you want to run d3.csv once, get the data and save them elsewhere, you can try something like this or this or this

In general:

// define your module here    
var Module_BarDataDaily = function(data) {
   this.dataX = data; //this.dataX will store the data
   this.process = function(){// do whatever you need afterwards
            console.log(this.dataX);
    }
 };

var process = function(myModule){
   // or use another function to do what you need etc
   console.log(myModule.dataX);
}

// load your csv once and save the data
d3.csv("path/to/your.csv", function(error, data) {
    // after some proper error handling...
    var myModule = new Module_BarDataDaily(data);
    // so then you can do anything after that and 
    // your myModule will have data without calling d3.csv again
    myModule.process();
    //or
    process(myModule);
});

Hope this helps! Good luck!

mkaran
  • 2,528
  • 20
  • 23
0

I've used the following based on mkaran's answer:

 var Module_BarDataDaily = function(data) {
     this.dataX = data;
 };


 d3.csv("data/BarChart_data.csv", function(error, rows) {
     var myModule = new Module_BarDataDaily(rows);

     var chart = barChart();
     chart.render(myModule.dataX);

     d3.selectAll('input[name="meas"]').on("change", function change() {
         chart.currentMeasure(this.value)
         chart.render(myModule.dataX);
     });

 });
whytheq
  • 34,466
  • 65
  • 172
  • 267