Chartjs-Chart.js scatter chart stops working after extending to multiple datasets

0👍

This is slightly out of scope, since the solution below does not use the data structure in my original question. However, as I believe my original method was much flawed in that it used nested API requests, I’m posting this answer that I believe achieves what I’m trying to do in a good manner.

My backend uses MongoDB, and this aggregation yielded a suitable data structure:

Observation.aggregate([{
  $group: {
    _id: "$location.name",
    temperatures: {
      $push: { "temperature": "$temperature" },
    },
    dates: {
      $push: { "date": "$date" },
    },
  }
}]);

(N.B., $location.name takes unique values so I’m using it as $group._id to strip a few lines of code.)

With that, it became straightforward to create the chart like so:

ngAfterViewInit() {
  this.observationService.getAllGrouped()
    .subscribe((locations: any) => {
      let datasets = [];

      locations.forEach(location => {
        let temperatures = location.temperatures.map(t => t.temperature);
        let dates = location.dates.map(d => d.date);
        let data = [];

        temperatures.forEach((temperature, i) => {
          data.push({ x: dates[i], y: temperature });
        });

        datasets.push({ label: location._id, data: data });
      });

      const data = { datasets: datasets };
      const options = {
        scales: {
          xAxes: [{
            type: 'time',
          }],
        },
      };

      this.chart = new Chart('canvas', { type: 'scatter', data: data, options: options });
    });
}

1👍

Try to create a chart after component’s view is initialized, thus use ngAfterViewInit instead of ngOnInit.

Update:
In the case where you want to plot all the datasets, you have extra subscribe. Here:

    locations.forEach((location: any) => {
            this.observationService.getAllByLocation(location)
  ---->       .subscribe(observations => {
                datasets.push(this.createDataset(observations));
              });
          });

which means that line:

  this.chart = new Chart('temperatureChart', { type: 'scatter', data: data, options: options });

will be called before datasets is filled with data.

One possible solution would be to add data series in the subsribe above like:

locations.forEach((location: any) => {
  this.observationService.getAllByLocation(location)
  .subscribe(observations => {
    this.chart.addSerie({
        data: this.createDataset(observations)
      },
      true,
      true);
  });
});

however, the resulting plot will be updated few times (you will have to wait until it loads fully)

Second one, you could create a chart once the subsribe is finished, like:

locations.forEach((location: any) => {
  this.observationService.getAllByLocation(location)
    .subscribe(observations => {
      datasets.push(this.createDataset(observations));
    },
      () => { /*error handling */},
      () => { /*observable is finished your datasets is filled 
with data, here you can create a graph*/});
});

Leave a comment