You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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变更检测依赖引用变化触发:

  1. DataService的getData()仅在初始化时推送一次数组引用,后续setInterval仅对同一数组做shift()/push()操作(数组引用未变)
  2. 主组件的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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.23 23:04:53