Angular中向图表标签数组push值后无法在图表正常显示问题排查
问题描述
我尝试向图表标签数组中push值,但操作完成后这些值没有显示在图表上,是哪里操作有误?以下是我的代码:
public barChartLabels: Label[] = []; this.mailboxTags.getTotalPending().subscribe((tags: Tag[]) => { for (let index = 0; index < tags.length; index++) { if(tags[index].type == 'category') { this.barChartLabels.push(tags[index].name); } } });
push值完成后我打印barChartLabels,输出内容如下,数据是正确的:
0: "general" 1: "Software" length: 2 [[Prototype]]: Array(0)
但这些值没有作为标签显示在柱状图上。如果我硬编码赋值:
public barChartLabels: Label[] = ['general', 'software'];
图表就能正常显示标签。我还试过如下写法,依然无效:
this.barChartLabels.push(tags[index].name.toString());
补充:后续发现数据本身是写入成功的,只是页面加载后变更不会立刻生效,需要点击页面任意位置才会显示变更内容。
问题原因
- 多数Angular生态的图表组件(如ng2-charts)的输入属性是基于引用变更触发重渲染的,你使用
push方法修改原数组时,数组的内存引用没有发生变化,组件默认不会检测到数组内部元素的变更,因此不会重新渲染图表。 - 你的异步请求回调如果脱离了Angular的NgZone监控范围,回调内修改数据后Angular不会自动触发全局变更检测,只有手动交互(如点击页面)时才会触发变更检测,因此会出现点击后才显示内容的情况。
- 硬编码赋值能正常显示的原因是初始化阶段就完成了赋值,组件首次渲染时已经拿到了完整的标签数据。
解决方案
- 优先替换数组引用,不要用push修改原数组
将你的循环push逻辑改写为筛选后直接赋值,直接替换整个数组的引用,触发图表组件的重渲染:
this.mailboxTags.getTotalPending().subscribe((tags: Tag[]) => { this.barChartLabels = tags .filter(tag => tag.type === 'category') .map(tag => tag.name); });
- 如果修改后仍然需要点击页面才生效,手动触发变更检测即可,有两种可选方案:
- 方案一:使用NgZone包裹赋值逻辑
首先在当前组件的构造函数中注入NgZone:
改写回调逻辑:constructor(private ngZone: NgZone) {}this.mailboxTags.getTotalPending().subscribe((tags: Tag[]) => { this.ngZone.run(() => { this.barChartLabels = tags .filter(tag => tag.type === 'category') .map(tag => tag.name); }) }); - 方案二:使用ChangeDetectorRef标记组件需要变更检测
首先在当前组件的构造函数中注入ChangeDetectorRef:
改写回调逻辑:constructor(private cdr: ChangeDetectorRef) {}this.mailboxTags.getTotalPending().subscribe((tags: Tag[]) => { this.barChartLabels = tags .filter(tag => tag.type === 'category') .map(tag => tag.name); this.cdr.markForCheck(); });
- 方案一:使用NgZone包裹赋值逻辑
内容的提问来源于stack exchange,提问作者andyiscoding
相关产品推荐
相关产品推荐

