Chart.js 在同一组件中以 Angular 创建多个图表

问题描述 投票:0回答:3

我有一个带有组件的 Angular 6 项目,该组件获取由其父对象传递的图块对象。对于每个tile passend,我想用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>

Screnshot from missing charts

编辑

这是我编辑的打字稿代码。每个图块都有一个 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>

Picture of error codes

javascript typescript chart.js angular6
3个回答
0
投票

在模板 (html) 中,每个图表的画布 id 必须不同


0
投票

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

1.- 您可以生成一个仅用于渲染 ChartJS 图形的组件,例如,将其称为图表动态组件,其中包含多个输入 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();
  }

}

app-chart-动态 html 文件

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

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

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


0
投票

index.html

<h5 class="mb-2">Bar Chart</h5>
  <div style="width: 90%" class="mb-4">
    <canvas id="bar-chart">{{ barChart }}</canvas>
  </div>
  <h5 class="mb-2">Line Chart</h5>
  <div style="width: 90%" class="mb-4">
    <canvas id="line-chart">{{ lineChart }}</canvas>
  </div>

重要的是,当您初始化变量(“barChart”和“lineChart”)时,您可以使用任何变量,但在 TS 中我们想要输入所有内容,因此请按照这两行正确输入的方式

您的组件名称.ts

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

export class YOUR_COMPONENT_NAME implements OnInit {
  public barChart: chartjs<"bar", string[], string> | null = null;
  public lineChart: chartjs<"line", string[], string> | null = null;

  constructor() {}

  ngOnInit(): void {
    this.createChart()
  }


  public createChart() {
    this.barChart = new chartjs('bar-chart', {
      type: 'bar', 
      data: {
        labels: ['S1', 'S2', 'S3', 'S4',
          'S5', 'S6', 'S7'],
        datasets: [
          {
            label: "Entrainement",
            data: ['3', '3', '3', '2', '4', '4','3'],
            backgroundColor: '#C72864'
          },
          {
            label: "Alimentation",
            data: ['4', '2', '7', '4', '1', '5','7'],
            backgroundColor: '#157E39'
          }
        ]
      },
      options: {
        aspectRatio: 2.5,
        responsive:true,
        scales: {
          y: {
            title: {
              display:true,
              text:'Jours réusis',
              align: 'end',
            }

          },
          x: {
            position: 'right',
            title: {
              display:true,
              text:'N° semaine',
              align: 'end'
            }
          },
        }
      }
    });
    this.lineChart = new chartjs("line-chart", {
      type: 'line', 
      data: {
        labels: ['S1', 'S2', 'S3', 'S4',
          'S5', 'S6', 'S7', 'S8'],
        datasets: [
          {
            label: "poids",
            data: ['75', '90', '85', '77', '80', '55','75', '65'],
            borderColor: 'blue'
          },
          {
            label: "Tour de cuisses",
            data: ['52', '74', '65', '75', '40', '90','85', '77'],
            backgroundColor: 'limegreen'
          },
          {
            label: "Tour de d'épaules",
            data: ['80', '76', '28', '28', '65', '24','80', '76'],
            backgroundColor: 'limegreen'
          },
          {
            label: "Tour de bras",
            data: ['52', '74', '65', '75', '40', '90','85', '77'],
            backgroundColor: 'limegreen'
          }
        ]
      },
      options: {
        aspectRatio: 2.5,
        responsive:true,
      }
    });
  }
}
© www.soinside.com 2019 - 2024. All rights reserved.