Angular中ngOnChanges未检测到组件数据变更问题排查
Angular图表组件ngOnChanges未检测到数据变更排查
场景
- 数据表、图表展示最近30分钟的时序数据,包含数据时间与0-999的随机数值
- 每10秒向数组添加新对象并删除最旧对象,始终仅展示最近30分钟数据,最新值标签显示新增的最新值
- 主组件每10秒更新数据数组,但图表组件的
ngOnChanges未触发,图表无法更新重绘
原始代码片段
主组件TS
export class MainComponent implements OnInit { data: IData[] = []; latest: any = { time: 0, value: 0 }; constructor(private dataService: DataService, private datePipe: DatePipe) {} ngOnInit(): void { this.getData(); this.getLatestValue(); } getData() { this.dataService.getData().subscribe((data) => { this.data = data; }); } }
主组件HTML
<div class="row"> <app-table (editedRow)="onRowEdit($event)" [tableData]="data"></app-table> <div class="column"> <app-label [latestTime]="latest.time" [latestValue]="latest.value" ></app-label> <app-chart [chartData]="data"></app-chart> </div> </div>
图表组件TS
export class ChartComponent implements OnChanges, OnInit { @Input() chartData: IData[] = []; data: any; constructor(private datePipe: DatePipe) {} ngOnChanges(changes: SimpleChanges): void { console.log(changes); this.initChart(); } ngOnInit(): void { this.initChart(); } initChart() { const times = this.chartData.map((item) => this.datePipe.transform(item.time, "yyyy-MM-dd HH:mm:ss") ); const values = this.chartData.map((item) => item.value.toFixed(2)); this.data = { labels: times, datasets: [ { label: "First Dataset", data: values, fill: false, borderColor: "", tension: 0.4, }, ], }; } }
DataService代码
import { Injectable } from "@angular/core"; import { Observable, Subject } from "rxjs"; import { IData } from "./interface"; const delta = 10000; function getValue(): number { return Math.random() * 1000; } function getTime(index: number) { return Date.now() - index * delta; } @Injectable({ providedIn: "root", }) export class DataService { private data: IData[] = []; private lastValueData: Subject<IData>; constructor() { this.lastValueData = new Subject(); for (let i = 179; i >= 0; i--) { this.data.push({ time: getTime(i), value: getValue(), }); } setInterval(() => { const value = { time: Date.now(), value: getValue(), }; this.data.shift(); this.data.push(value); this.lastValueData.next(value); }, delta); } public stream(): Observable<IData> { return this.lastValueData.asObservable(); } public getData(): Observable<IData[]> { return new Observable((subscriber) => { subscriber.next(this.data); subscriber.complete(); }); } }
问题原因
Angular变更检测依赖引用变化触发:
- DataService的
getData()仅在初始化时推送一次数组引用,后续setInterval仅对同一数组做shift()/push()操作(数组引用未变) - 主组件的
data变量始终指向同一个数组对象,导致传递给图表组件的chartData输入属性引用无变化,ngOnChanges不会触发
修复方案
方案1:更新主组件订阅逻辑,创建新数组引用
修改主组件,订阅DataService的stream()获取最新数据,每次更新时创建新数组(改变引用):
import { Subscription } from 'rxjs'; export class MainComponent implements OnInit, OnDestroy { data: IData[] = []; latest: any = { time: 0, value: 0 }; private dataSub?: Subscription; constructor(private dataService: DataService, private datePipe: DatePipe) {} ngOnInit(): void { // 初始化获取初始数据,创建新数组 this.dataService.getData().subscribe((initialData) => { this.data = [...initialData]; }); // 订阅最新数据流,每次更新都生成新数组 this.dataSub = this.dataService.stream().subscribe((newItem) => { // 移除最旧元素,添加新元素,创建新数组 this.data = [...this.data.slice(1), newItem]; this.latest = newItem; }); } ngOnDestroy(): void { // 清理订阅,避免内存泄漏 this.dataSub?.unsubscribe(); } }
方案2:图表组件使用IterableDiffers检测数组内容变化
如果不想修改数组引用,可在图表组件中通过IterableDiffers监听数组内容变化,替代ngOnChanges:
import { DoCheck, IterableDiffers } from '@angular/core'; export class ChartComponent implements OnInit, DoCheck { @Input() chartData: IData[] = []; data: any; private differ: any; constructor(private datePipe: DatePipe, private differs: IterableDiffers) { this.differ = differs.find([]).create(); } ngOnInit(): void { this.initChart(); } ngDoCheck(): void { // 检测数组内容变化 const changes = this.differ.diff(this.chartData); if (changes) { this.initChart(); } } initChart() { const times = this.chartData.map((item) => this.datePipe.transform(item.time, "yyyy-MM-dd HH:mm:ss") ); const values = this.chartData.map((item) => item.value.toFixed(2)); this.data = { labels: times, datasets: [ { label: "First Dataset", data: values, fill: false, borderColor: "", tension: 0.4, }, ], }; } }
内容的提问来源于stack exchange,提问作者krk5570
相关产品推荐
相关产品推荐

