【问题标题】:Charts.js dynamic data call (Angular)Charts.js 动态数据调用(Angular)
【发布时间】:2020-11-23 08:50:45
【问题描述】:

问题来了: 我正在尝试从我的 api 动态调用数据以将其放入我的 chart.js 中。 Chart.js 需要 Json 才能工作,当我向它发送本地 Json 时,一切正常,但是当我尝试从 api 向它发送 json 时,我遇到了困难...... 下面的代码:

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>

资源控制台:

如您所见,控制台中的所有内容(对象和 Json)都清晰可见,并且没有显示错误。 但是图表是空的... 有没有人有想法? 谢谢

【问题讨论】:

  • 除了 async 变量及其用法的问题外,尝试this.mercure.filter() 表明传入的数据是 JS 数组而不是对象。此外,您正在使用JSON.stringify 序列化数组。它将使数组成为字符串,因此表达式 this.mercureJson.tempMoythis.mercureJson.tempMax 无效。请附上控制台日志完整输出的屏幕截图。
  • 屏幕正下方

标签: node.js angular mongodb charts chart.js


【解决方案1】:

根据我的评论展开,这里有多个问题

  1. 数据是异步分配的。您需要确保它在 this.data 初始化时可用。您可以使用高阶映射运算符 switchMap 使一个 observable 依赖于另一个。

  2. 您正在尝试从通过序列化数组生成的字符串访问属性。您需要访问对象元素以获取它的属性。

试试下面的

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)
      )
    )
  }
}

更新:使用多元素数组

正如评论中所说,您可以使用Array#map 将数组中的所有元素转换为 ChartJS 期望的格式。

试试下面的

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
          }))
      )
    )
  }
}

【讨论】:

  • 我没有在页面底部看到您的评论(使用数组的第一个元素(数组显然只有一个元素))。我确实有几个元素要在 chart.js 中发送,但我不能将它们放在数组 [0] 中,它似乎只接受一个元素。我应该在别处指定吗?
  • 在这种情况下,您可以映射数组中的元素以返回 ChartJS 预期格式的对象。
  • 嗨 Michel D,我刚刚更新了我的代码,一切正常,非常感谢你的回答,你为我节省了很多时间,让我的代码更简洁 :)
猜你喜欢
  • 2014-02-18
  • 2018-02-18
  • 2014-12-08
  • 2023-01-30
  • 2018-12-18
  • 1970-01-01
  • 2020-10-03
  • 2023-02-22
  • 2017-02-26
相关资源
最近更新 更多