首页 > 解决方案 > Chart.js 在同一组件中的 Angular 中创建多个图表

问题描述

我有一个带有组件的 Angular 6 项目,该组件获取其父对象传递的瓦片对象。对于每个图块传递,我想用 chart.js 生成相同的图表。我在第一个图表上工作得很好,但所有其他图表都没有被渲染。控制台错误代码: Failed to create chart: can't acquire context from the given item

我的 tile.component.html

<div *ngIf="tile.type === 'tileImg'">
  <div class="card custom-card"
       routerLinkActive="glowing">
    <img class="card-img-top rounded" src="{{ tile.imgPath }}" alt="Tile image" />
    <div class="card-body">
      <p class=" card-text text-center">{{ tile.name }}</p>
    </div>
  </div>
</div>
<div *ngIf="tile.type === 'tileChart'">
  <div class="card custom-card"
       routerLinkActive="glowing">
    <div>
      <canvas id="canvas">{{ chart }}</canvas>
    </div>
    <div class="card-body">
      <p class=" card-text text-center">{{ tile.name }}</p>
    </div>
  </div>
</div>

我的 tile.component.ts - 不要介意评论,仅用于测试目的

import { Component, OnInit, Input } from '@angular/core';
import { Chart } from 'chart.js';

import { Tile } from 'src/app/tile-container/tile/tile.model';
//import { TileChart } from 'src/app/tile-container/tile/tile-chart.model';

@Component({
  selector: 'app-tile',
  templateUrl: './tile.component.html',
  styleUrls: ['./tile.component.css']
})
export class TileComponent implements OnInit {
  @Input() tile: Tile;
  //tileChart: TileChart;
  chart = [];

  constructor() { }

  ngOnInit() {
    //console.log(this.tile);
    //console.log(this.tile.getType());
    //console.log(this.tile.getChartType() + "   " + this.tile.getChartData() + "  " + this.tile.getType().localeCompare('tileChart'));
    //console.log(this.tile.getType() == 'tileChart');
    if (this.tile.getType() == 'tileChart') {
      this.generateChart(this.tile.getChartType(), this.tile.getChartData());
      
    }
  }

  generateChart(chartType: string, chartData: number[]) {
    

    this.chart = new Chart('canvas', {
      type: chartType,
      data: {
        datasets: [{
          data: chartData,
          backgroundColor: ['#F39E01', '#b8bbc1']
        }],
        labels: [
          'Verbrauch diese Woche',
          'Einsparung in kWh'
        ]
      },
      options: {
        legend: {
          display: false,
        },
        rotation: 1.1 * Math.PI,
        circumference: 0.8 * Math.PI
      }
    });
  }

}

和父 tile-container.component.html - 没有必要

<div class="container custom-container">
  <div class="container-heading">
    <h2>{{ tileContainer.name }}</h2>
  </div>
  <hr />
  <div class="row">
    <div class="col text-center"
         *ngFor="let tile of tileContainer.tiles">
      <app-tile      
        [tile]="tile">
      </app-tile>
    </div>
  </div>
</div>

缺失图表的截图

编辑

这是我编辑的打字稿代码。每个图块都有一个 id,我试图用它来为每个创建的图表创建一个唯一的 id。

ngOnInit() {
    console.log(this.tile.id);
    if (this.tile.getType() == 'tileChart') {
      this.chartId = this.tile.id.toString();
      this.ctx = document.getElementById(this.chartId);
      console.log(this.ctx);
      this.generateChart(this.tile.getChartType(), this.tile.getChartData());
    }
 }

这是我使用数据绑定的 html。

<div>
  <p>{{ chartId }}</p>
  <canvas id="{{ chartId }}">{{ chart }}</canvas>
</div>

错误代码图片

标签: javascripttypescriptchart.jsangular6

解决方案


我将为您提供不同的方法第一个是最简单的另一种您需要更多知识...希望对您有所帮助

1.- 您可以生成一个仅用于渲染 chartjs 图形的组件,例如将其称为 chart-dynamic 并使用多个输入 id 来获取渲染多个图表所需的唯一 id,并使用 dataChart 来渲染所有完全对象,假设您的瓦。组件看起来像这样

重要的!!您的 dataChart 必须像一个对象数组一样思考,每个对象基本上都是您将呈现到模板中的图表(遵循 chartJs 的官方文档)

<div *ngIf="tile.type === 'tileImg'">
  <div class="card custom-card"
       routerLinkActive="glowing">
    <img class="card-img-top rounded" src="{{ tile.imgPath }}" alt="Tile image" />
    <div class="card-body">
      <p class=" card-text text-center">{{ tile.name }}</p>
    </div>
  </div>
</div>
<div *ngIf="tile.type === 'tileChart'">
  <div class="card custom-card"
       routerLinkActive="glowing">
<!-- NEW CODE -->

<ng-container *ngIf="dataChart?.length > 0" >
<div *ngFor="let chart of dataChart; let i=index">
    <app-chart-dynamic [id]="SomeUniqueID" [dataChart]="chart" [type]="chart.type"></app-chart-dynamic>
</div>
</ng-container>

<!-- Finish here -->
    <div class="card-body">
      <p class=" card-text text-center">{{ tile.name }}</p>
    </div>
  </div>
</div>

在您的 tile.component.ts 生成数据方法作为对象数组,将 generateChart 函数移动到新组件中

import { Component, OnInit, Input } from '@angular/core';
import { Chart } from 'chart.js';

import { Tile } from 'src/app/tile-container/tile/tile.model';
//import { TileChart } from 'src/app/tile-container/tile/tile-chart.model';

@Component({
  selector: 'app-tile',
  templateUrl: './tile.component.html',
  styleUrls: ['./tile.component.css']
})
export class TileComponent implements OnInit {
  @Input() tile: Tile;
  //tileChart: TileChart;
  chart = [];
  public dataChart: [];

  constructor() { }

  ngOnInit() {
    //console.log(this.tile);
    //console.log(this.tile.getType());
    //console.log(this.tile.getChartType() + "   " + this.tile.getChartData() + "  " + this.tile.getType().localeCompare('tileChart'));
    //console.log(this.tile.getType() == 'tileChart');
    this.getCharts();
  }

  public getCharts() { 
    // call data from you service or data mock
    this.dataChart = {....response};
  }


}

现在假设你已经创建了你的新组件应该是这样的(你已经导入了 charJs 和其他东西)

import { Component, OnInit, Input, ViewChild, ElementRef, AfterViewInit } from '@angular/core';
import { Chart } from 'chart.js';

@Component({
  selector: 'app-chart-dynamic',
  templateUrl: './chart-dynamic.component.html',
  styleUrls: ['./chart-dynamic.component.css']
})
export class ChartDynamic implements OnInit, AfterViewInit {

  @Input() datasChart: any;
  @Input() id: string;
  @Input() type?: string;
  public idChart: any;
  @ViewChild('chart') chart: ElementRef;
  public chartObject: any;

  constructor() { }

  ngOnInit() {

  }

  generateChart(id: string ,chartType?: string, chartData: any) {
    this.idChart = this.id;
    this.chart = new Chart(`${this.idChart}`,  this.datasChart );
  }

  ngAfterViewInit() {
    this.drawGraphics();
  }

}

应用程序图表动态 html 文件

<div class="some-class-style" >
    <canvas [id]="id" #chart> {{ chart }}</canvas>
</div>

如果您添加到模块等中,它应该可以工作

另一种方法是将 viewChild 和 viewChildren 与工厂解析器结合起来,它更复杂但功能更强大,您应该根据角度文档检查第一次


推荐阅读