0

i am trying to make a website showing a graph based on some user uploaded json files. I am using a fetch to get the JSON data from a servlet and i make the graph inside the fetch, this works fine. Only i'd like to make a addData() function to add extra data to the same plot. The problem is i can't access the data outside of the fetch request and i can't acces the addData() function from my html page if it's in the fetch in my javascript file. Is there a good way to approach this problem?

my JavaScript code:


fetch('/json_storage')
    .then((response) => {
        return response.json();
    })
    .then((myJson) => {
        let hrdata = myJson[0].heartRate;
        let timestamp = hrdata.map(t => { return t.timestamp });

        let bpm = hrdata.map( b => {return b.bpm});


        let ctx = document.getElementById('canvas').getContext('2d');
        let canvas = new Chart(ctx, {
            type: 'line',
            data: {
                labels: timestamp,
                datasets: [{
                    label: new Date(timestamp[0]),
                    data: bpm,
                    backgroundColor: 'rgba(255,0,55,0.25)',
                    pointRadius: 0
                }]
            },
            options: {
                title: {
                    display: true,
                    text: 'The course of your heart rate during the day'
                },
                scales: {
                    xAxes: [{
                        type: 'time',
                        time: {
                            unit: 'hour'
                        }
                    }]
                }
            }
        });

        function addData() {
            for (let i =1; i < myJson.length; i++){
                let hrdata = myJson[i].heartRate;
                let bpm = hrdata.map( b => {return b.bpm});
                let timestamp = hrdata.map(t => { return t.timestamp });

                canvas.data.data = bpm;
                canvas.data.labels = timestamp;

                canvas.update();

            }
        }

    });


and my HTML code:

<button id="addData">Add dataset</button>
<canvas id="canvas" width="300" height="300"></canvas>
BenB
  • 3
  • 1
  • Does this answer your question? [How do I return the response from an asynchronous call?](https://stackoverflow.com/questions/14220321/how-do-i-return-the-response-from-an-asynchronous-call) – evolutionxbox Jan 25 '20 at 16:26

2 Answers2

0

In your code, the addData and myJson are scoped inside your promise callback, which is not available outside - either you need to expose those to the global context or write a reusable function which exposes (returns) the addData.

I modified your fetch function to a reusable class, where you have access to all the data from its context.

class LineChart {
  constructor(ctx, dataUrl, initialData = []) {
    this.ctx = ctx;
    this.data = initialData;
    this.dataUrl = dataUrl;
    this.canvas = null;
    
    this.fetchData();
  }

  fetchData = () => fetch(this.dataUrl)
    .then((response) => {
      return response.json();
    })
    .then((myJson) => {
      this.data = myJson;
      this.draw();
      return this.data;
    });

  draw = () => {
    if (!this.ctx) {
      return false;
    }

    let hrdata = this.data[0].heartRate;
    let timestamp = hrdata.map(t => {
      return t.timestamp
    });

    let bpm = hrdata.map(b => {
      return b.bpm
    });

    this.canvas = new Chart(this.ctx, {
      type: 'line',
      data: {
        labels: timestamp,
        datasets: [{
          label: new Date(timestamp[0]),
          data: bpm,
          backgroundColor: 'rgba(255,0,55,0.25)',
          pointRadius: 0
        }]
      },
      options: {
        title: {
          display: true,
          text: 'The course of your heart rate during the day'
        },
        scales: {
          xAxes: [{
            type: 'time',
            time: {
              unit: 'hour'
            }
          }]
        }
      }
    });
  }
  
  addData = () => {
    alert(`in addData function ${JSON.stringify(this.data)}`);

    for (let i = 1; i < this.data.length; i++) {
      let hrdata = this.data[i].heartRate;
      let bpm = hrdata.map(b => {
        return b.bpm
      });
      let timestamp = hrdata.map(t => {
        return t.timestamp
      });

      this.canvas.data.data = bpm;
      this.canvas.data.labels = timestamp;

      this.canvas.update();
    }
  }
}


//usage
const chart = new LineChart(
  document.getElementById('canvas').getContext('2d'),
  '/json_storage'
);

document.getElementById('addData').onclick = chart.addData;
<button id="addData">Add dataset</button>
<canvas id="canvas" width="300" height="300"></canvas>
ajai Jothi
  • 2,284
  • 1
  • 8
  • 16
  • hi, i tried this solution but keep getting the following error `Uncaught (in promise) ReferenceError: ctx is not defined at LineChart.draw` – BenB Jan 25 '20 at 19:49
  • unfortunately it still wont let me access the canvas to push the new dataset – BenB Jan 26 '20 at 16:18
  • @BenB sorry! didn't notice the canvas context. now updated the code. Try now – ajai Jothi Jan 26 '20 at 17:43
0

To do what you want would require you to either store the data that's been built so far in a data structure that is persistent in your page. If you are using a UI framework like React or Angular then you can store it as part of your state, then when someone clicks the "Add Data" button, call a function to modify that state data accordingly and re-render the results.

Another way would be to re-fetch the data and combine it with the added data, but then you would lose any previously added data.

If you're not using a UI framework, have you tried simply keeping the fetched data in a variable defined at the top of your javascript file? After the fetch, set it in that variable and have a function that does the chart generation separately. Each time the "Add Data" button is clicked, it should first add the data to the previously saved data, and re-render the chart based on the new dataset.

You'll need to separate the storage of the data and the canvas from the processing of the data so that when new data is added, the canvas can be updated, but that's outside of the scope of the question you're asking, but perhaps something like this would be workable:

const allJson = [];
const ctx = document.getElementById("canvas").getContext("2d");
const canvas = new Chart(ctx, {
  type: "line",
  data: {
    labels: timestamp,
    datasets: [
      {
        label: new Date(timestamp[0]),
        data: bpm,
        backgroundColor: "rgba(255,0,55,0.25)",
        pointRadius: 0
      }
    ]
  },
  options: {
    title: {
      display: true,
      text: "The course of your heart rate during the day"
    },
    scales: {
      xAxes: [
        {
          type: "time",
          time: {
            unit: "hour"
          }
        }
      ]
    }
  }
});

fetch("/json_storage")
  .then(response => response.json())
  .then(myJson => {
    addData(myJson[0]);
  });

function addData(newJson) {
  allJson.push(newJson);
  let bpm = [];
  let timestamp = [];
  for (let i = 0; i < allJson.length; i++) {
    let hrdata = myJson[i].heartRate;
    bpm = bpm.concat(hrdata.map(b => b.bpm));
    timestamp = timestamp.concat(hrdata.map(t => t.timestamp));
  }

  canvas.data.data = bpm;
  canvas.data.labels = timestamp;

  canvas.update();
}

Note that I'm just guessing at the right way to prepare the data for the chart since I don't know the exact form of the data coming from the fetch and how it's all structured. But the general idea is:

  • Setup variables for the data array and the canvas
  • Do the initial fetch and set it using addData
  • Each time you add more data it should extend and display it
Always Learning
  • 5,510
  • 2
  • 17
  • 34