Angular中如何在所有数据就绪后加载渲染NG2Charts图表
问题原因
图表显示空白的核心原因有两点:
- 组件初始化阶段,
doughnutChartData的datasets.data读取的是三个未赋值的变量,值为undefined,图表会直接用这份空数据源完成首次渲染。 - 异步请求返回后,仅修改了三个独立的数值变量,既没有更新图表绑定的
doughnutChartData数据源,也没有触发图表重绘,图表无法自动感知到外部变量的变化。
此外代码中存在两处会导致运行报错的笔误:
ngOnInit生命周期中调用的方法名为getEmpleados(),和实际定义的getEmployee()不匹配- 请求回调内使用了未定义的变量
empleados,和入参名employees不一致
解决方案
以下两种方案任选其一即可,推荐优先使用第一种,逻辑更简单不易出错。
方案1:控制渲染时机,数据加载完成前不初始化图表
通过加载标记控制canvas元素的渲染,等所有异步数据准备完成后,再初始化图表,从根源避免空值渲染:
- 在组件类中新增布尔类型的加载标记,初始值为
false - 初始化图表配置时,
datasets.data先设为空数组 - 异步请求拿到数据后,先更新图表配置里的数据源,再把加载标记设为
true - 模板中给canvas元素添加
*ngIf判断,只有加载标记为true时才渲染图表元素
修正后的组件代码(detail.component.ts)
import { Component, OnInit, ViewChild } from '@angular/core'; import { ChartData, ChartType } from 'chart.js'; import { BaseChartDirective } from 'ng2-charts'; // 这里记得补全你自己的employee服务引入 @Component({ selector: 'app-detail', templateUrl: './detail.component.html' }) export class DetailComponent implements OnInit { empHoursLearning!: number; empHProject1!: number; empHProject2!: number; // 新增加载标记 isDataLoaded = false; public doughnutChartLabels: string[] = [ 'Hours learning', 'Hours Project 1', 'Hours Project 2' ]; public doughnutChartData: ChartData<'doughnut'> = { labels: this.doughnutChartLabels, datasets: [ { // 初始化为空数组 data: [], backgroundColor: ['#7DFF93', '#36A2EB', '#FFCE56'], hoverBackgroundColor: '#E6E6E6', hoverBorderColor: '#E6E6E6', }, ], }; public doughnutChartType: ChartType = 'doughnut'; @ViewChild(BaseChartDirective) chart?: BaseChartDirective; ngOnInit(): void { // 修正方法名笔误 this.getEmployee(); } async getEmployee() { const employees = await this._employeeService.getEmployee(); this.empHoursLearning = employees!['hl']; this.empHProject1 = employees!['p1']; // 修正变量名笔误 this.empHProject2 = employees!['p2']; // 更新图表绑定的数据源 this.doughnutChartData.datasets[0].data = [ this.empHoursLearning, this.empHProject1, this.empHProject2 ]; // 标记数据加载完成,触发图表渲染 this.isDataLoaded = true; } }
修正后的模板代码(detail.component.html)
只需要给canvas标签添加*ngIf判断即可:
<div class="container mt-4"> <div class="card"> <div class="card-body"> <div class="container d-flex"> <span class="h3">Employee detail</span> </div> <div> <div> <div style="display: block"> <canvas baseChart *ngIf="isDataLoaded" [data]="doughnutChartData" [type]="doughnutChartType"> </canvas> </div> </div> </div> </div> </div> </div>
方案2:保留图表初始渲染,数据返回后手动触发更新
如果需要先显示图表占位,可以在数据返回后,手动调用图表实例的update()方法触发重绘:
- 同样需要先更新
doughnutChartData.datasets[0].data的实际值,注意不能只修改三个独立变量,因为初始化时变量值已经被传入数组,后续单独修改变量不会同步更新数组内容 - 调用
@ViewChild获取到的图表实例的update()方法强制刷新
对应修改getEmployee方法即可:
async getEmployee() { const employees = await this._employeeService.getEmployee(); this.empHoursLearning = employees!['hl']; this.empHProject1 = employees!['p1']; this.empHProject2 = employees!['p2']; // 更新图表数据源 this.doughnutChartData.datasets[0].data = [ this.empHoursLearning, this.empHProject1, this.empHProject2 ]; // 手动触发图表重绘 this.chart?.update(); }
内容的提问来源于stack exchange,提问作者AhmedSHA256
相关产品推荐
相关产品推荐

