0

Here is the problem: I'm trying to dynamically call data from my api to put it in my chart.js. Chart.js needs Json to work, when I send it a local Json everything works fine but when I try to send it a json from the api I have difficulty ... the code below:

Ts:

import {Component, OnDestroy, OnInit} from '@angular/core';
import {NbColorHelper, NbThemeService} from '@nebular/theme';
import {Planet} from '../../../../../@core/models/planet.model';
import {PlanetService} from '../../../../../@core/services/planet.service';

@Component({
  selector: 'ngx-chart-comparatif',
  templateUrl: './chart-comparatif.component.html',
  styleUrls: ['./chart-comparatif.component.scss'],
})
export class ChartComparatifComponent implements OnInit, OnDestroy {

  data: any;
  options: any;
  themeSubscription: any;
  mercure: Array<Planet>;
  mercureJson: any = <Planet> {}; // I think the problem is from here.I've tried lots of solutions but none of them work

  jsonLocal = {'posi': 5, 'size': 8, 'long': 8}; // Json local for tests (it works correctly)

  constructor(private theme: NbThemeService,
              private planetService: PlanetService) {
    this.themeSubscription = this.theme.getJsTheme().subscribe(config => {

      const colors: any = config.variables;
      const chartjs: any = config.variables.chartjs;

      this.data = {
        labels: ['Temperature Maximum', 'Temperature Moyenne', 'Temperature Minimum'],
        datasets: [{
          data: [this.mercureJson.tempMin, this.mercureJson.tempMoy, this.mercureJson.tempMax], // test
          label: 'Mercure',
          backgroundColor: 'rgba(143, 155, 179, 0.24)',
          borderColor: '#c5cee0',
        }, {
          data: [this.mercureJson.tempMin, this.mercureJson.tempMoy, this.mercureJson.tempMax], // test
          label: 'Venus',
          backgroundColor: 'rgba(255, 170, 0, 0.24)',
          borderColor: '#ffaa00',
        }, {
          data: [this.mercureJson.tempMin, this.mercureJson.tempMoy, this.mercureJson.tempMax], // test
          label: 'Terre',
          backgroundColor: 'rgba(0, 149, 255, 0.48)',
          borderColor: '#0095ff',
        }, {
          data: [this.mercureJson.tempMin, this.mercureJson.tempMoy, this.mercureJson.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() {
    this.planetService.getPlanets().subscribe(res => {
      this.mercure = res.data;
      this.mercure =  this.mercure.filter(e =>
        e.name === 'Mercure'); // the mercure array is filtered for the planet mercury only
      const format = JSON.stringify(this.mercure); // Format Json
      this.mercureJson = format;
      // tslint:disable-next-line:no-console
      console.log('test', this.mercure, 'test2', this.mercureJson );
    });
  }

  ngOnInit() {
    this.getPlanet();
  }

  ngOnDestroy(): void {
    this.themeSubscription.unsubscribe();
  }
}

HTML:

<h5 class="text-center">Température des planètes tellurique</h5>
<chart type="line" [data]="data" [options]="options"></chart>

Res console:

display of the mercury array in object and json

As you can see everything is clearly visible in the console (object and Json) and no error is displayed. But the Chart is empty ... Does anyone have an idea ?? thank you

2
  • Besides the issue with the async variable and it's usage, attempting this.mercure.filter() suggests that the incoming data is a JS array and not an object. Moreover you're serializing the array using JSON.stringify. It'll make the array a string, so expressions this.mercureJson.tempMoy and this.mercureJson.tempMax are invalid. Please include a screenshot of the complete output of the console log. Commented Nov 23, 2020 at 9:04
  • The screen just below Commented Nov 23, 2020 at 9:20

1 Answer 1

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
          }))
      )
    )
  }
}
Sign up to request clarification or add additional context in comments.

3 Comments

I hadn't seen your comment at the bottom of the page (use the first element of the array (array apparantly has only one element)). I have indeed several elements to send in the chart.js but I cannot put them in the array [0], it seems that it accepts only one element. Should I specify it elsewhere?
In that case you could map the elements in the array to return objects in the format expected by the ChartJS.
Hi Michel D, I just updated my code and everything works perfectly, thank you very much for your answers, you saved me a lot of time and made my code much cleaner :)

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.