我有一个带有组件的 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>
编辑
这是我编辑的打字稿代码。每个图块都有一个 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>
在模板 (html) 中,每个图表的画布 id 必须不同
我将为您提供不同的方法,第一个是最简单的,另一个您需要更多的知识......我希望它有帮助
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 与工厂解析器结合起来,它更复杂但更强大,您应该根据角度文档首先检查
<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 中我们想要输入所有内容,因此请按照这两行正确输入的方式
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,
}
});
}
}