Chartjs-Charts.js dynamic data call (Angular)

1👍

Expanding from my comment, there are multiple issues here

  1. The data is assigned asynchronously. You need to make sure it’s available by the time the this.data is initialized. You could use higher order mapping operator switchMap to make one observable depend on another.

  2. You’re trying to access properties from a string produced by serializing an array. You need to access the object element to get it’s properties.

Try the following

export class ChartComparatifComponent implements OnInit, OnDestroy {
  ...
  ngOnInit() {
    this.themeSubscription = this.getPlanet().pipe(
      switchMap(chartData => 
        this.theme.getJsTheme().pipe(
          map(config => ({ config: config, chartData: chartData}))  // <-- return data and config
        )
      )
    ).subscribe({
      next: ({config, chartData}) => {
        const colors: any = config.variables;
        const chartjs: any = config.variables.chartjs;

        this.data = {
          labels: ['Temperature Maximum', 'Temperature Moyenne', 'Temperature Minimum'],
          datasets: [{
            data: [chartData.tempMin, chartData.tempMoy, chartData.tempMax], // test
            label: 'Mercure',
            backgroundColor: 'rgba(143, 155, 179, 0.24)',
            borderColor: '#c5cee0',
          }, {
            data: [chartData.tempMin, chartData.tempMoy, chartData.tempMax], // test
            label: 'Venus',
            backgroundColor: 'rgba(255, 170, 0, 0.24)',
            borderColor: '#ffaa00',
          }, {
            data: [chartData.tempMin, chartData.tempMoy, chartData.tempMax], // test
            label: 'Terre',
            backgroundColor: 'rgba(0, 149, 255, 0.48)',
            borderColor: '#0095ff',
          }, {
            data: [chartData.tempMin, chartData.tempMoy, chartData.tempMax], // test
            label: 'Mars',
            backgroundColor: 'rgba(0, 214, 143, 0.24)',
            borderColor: '#00d68f',
          }],
        };

        this.options = {
          tooltips: {
            enabled: true,
            mode: 'single',
            position: 'nearest',
            callbacks: {
              label: function (tooltipItems) {
                return tooltipItems.yLabel + ' c°';
              },
            },
          },
          responsive: true,
          maintainAspectRatio: false,
          scales: {
            xAxes: [
              {
                gridLines: {
                  display: true,
                  color: chartjs.axisLineColor,
                },
                ticks: {
                  fontColor: chartjs.textColor,
                },
              },
            ],
            yAxes: [
              {
                gridLines: {
                  display: true,
                  color: chartjs.axisLineColor,
                },
                ticks: {
                  fontColor: chartjs.textColor,
                },
              },
            ],
          },
          legend: {
            labels: {
              fontColor: chartjs.textColor,
            },
          },
        };
      }
    })
  }

  getPlanet(): Observable<any> {      // <-- return the observable here
    this.planetService.getPlanets().pipe(
      map(res => 
        res.data.filter(e => e.name === 'Mercure')[0]     // <-- use the first element of the array (array apparantly has only one element)
      )
    )
  }
}

Update: use array with multiple element

As said in the comment, you could use Array#map to convert all the elements in the array to the format expected by ChartJS.

Try the following

export class ChartComparatifComponent implements OnInit, OnDestroy {
  ...

  planetOptions = {         // <-- object to hold planet specific properties
    Mercure: {
      backgroundColor: 'rgba(143, 155, 179, 0.24)',
      borderColor: '#c5cee0'
    },
    Venus: {
      backgroundColor: 'rgba(255, 170, 0, 0.24)',
      borderColor: '#ffaa00',
    },
    Terre: {
      backgroundColor: 'rgba(0, 149, 255, 0.48)',
      borderColor: '#0095ff'
    },
    Mars: {
      backgroundColor: 'rgba(0, 214, 143, 0.24)',
      borderColor: '#00d68f'
    }
  };

  ngOnInit() {
    this.themeSubscription = this.getPlanet().pipe(
      switchMap(chartData => 
        this.theme.getJsTheme().pipe(
          map(config => ({ config: config, chartData: chartData}))  // <-- return data and config
        )
      )
    ).subscribe({
      next: ({config, chartData}) => {
        const colors: any = config.variables;
        const chartjs: any = config.variables.chartjs;

        this.data = {
          labels: ['Temperature Maximum', 'Temperature Moyenne', 'Temperature Minimum'],
          datasets: chartData,        // <-- use modified chart data here
        };

        this.options = {
          tooltips: {
            enabled: true,
            mode: 'single',
            position: 'nearest',
            callbacks: {
              label: function (tooltipItems) {
                return tooltipItems.yLabel + ' c°';
              },
            },
          },
          responsive: true,
          maintainAspectRatio: false,
          scales: {
            xAxes: [
              {
                gridLines: {
                  display: true,
                  color: chartjs.axisLineColor,
                },
                ticks: {
                  fontColor: chartjs.textColor,
                },
              },
            ],
            yAxes: [
              {
                gridLines: {
                  display: true,
                  color: chartjs.axisLineColor,
                },
                ticks: {
                  fontColor: chartjs.textColor,
                },
              },
            ],
          },
          legend: {
            labels: {
              fontColor: chartjs.textColor,
            },
          },
        };
      }
    })
  }

  getPlanet(): Observable<any> {      // <-- return the observable here
    this.planetService.getPlanets().pipe(
      map(res =>
        res.data
          .filter(e => Object.keys(this.planetOptions).includes(e.name))   // <-- use only planets available in `this.planetOptions`
          .map(e => ({                                                     // <-- format expected by ChartJS
            ...this.planetOptions[e.name],
            data: [e.tempMin, e.tempMoy, e.tempMax],
            label: e.name
          }))
      )
    )
  }
}

Leave a comment