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

Angular路由实现后图表需窗口Resize才显示的问题排查

Angular路由切换后PrimeNG Chart图表不显示问题

在Angular项目中添加路由后,切换到图表页面时,基于Chart.js的PrimeNG Chart组件无法显示图表,只有调整窗口大小后才会加载出来。无路由时(标签页形式)图表可正常显示。需求是在保持Service处理数据的前提下解决该问题。

相关代码

Chart组件TS代码

export class ChartComponent implements OnInit, OnDestroy {
  showError: string;
  basicData: { labels: string[]; datasets: Dataset[] } = {
    labels: [],
    datasets: [],
  };
  private componentDestroyed$: Subject<boolean> = new Subject();
  weatherDataLoading: boolean;

  constructor(
    private weatherService: WeatherService,
    public datePipe: DatePipe
  ) {}

  ngOnInit(): void {
    this.weatherDataLoading = true;
    this.weatherService
      .getWeatherDataForChart()
      .pipe(
        finalize(() => (this.weatherDataLoading = false)),
        takeUntil(this.componentDestroyed$)
      )
      .subscribe({
        next: (data) => {
          this.basicData.labels = data.labels.map((time) =>
            this.datePipe.transform(time, 'shortTime')
          );
          this.basicData.datasets = data.datasets;
        },
        error: () =>
          (this.showError =
            'Something went wrong. Please try refreshing the page'),
      });
  }

  ngOnDestroy(): void {
    this.componentDestroyed$.next(true);
    this.componentDestroyed$.complete();
  }
}

Chart组件HTML代码

<div class="card">
  <div *ngIf="weatherDataLoading" class="load-container">
    <div class="load-speeding-wheel"></div>
  </div>
  <p-chart type="line" height="80vh" [data]="basicData"></p-chart>
</div>

Service中的getWeatherDataForChart函数

getWeatherDataForChart(): Observable<{
    labels: string[];
    datasets: Dataset[];
  }> {
    return this.getWeatherData().pipe(
      map((historicalWeatherData) => {
        const data = historicalWeatherData.hourly.temperature_2m.map(
          (value, i) => {
            return {
              temperature: value,
              time: historicalWeatherData.hourly.time[i],
            };
          }
        );
        const labels = data.map((item) => item.time);
        const datasets = [
          new Dataset(
            'Temperature',
            data.map((item) => item.temperature),
            false,
            '#42A5F5',
            0.4
          ),
        ];
        return { labels, datasets };
      })
    );
  }

Chart模块路由配置

const routes: Routes = [{ path: '', component: ChartComponent }];

@NgModule({
  declarations: [],
  imports: [CommonModule, RouterModule.forChild(routes)],
})
export class ChartModule {}

App路由配置

const appRoutes: Routes = [
  {
    path: '',
    redirectTo: '/weather-data',
    pathMatch: 'full',
  },
  {
    path: 'weather-data',
    loadChildren: () =>
      import('./modules/weather-data.module').then((m) => m.WeatherDataModule),
  },
  {
    path: 'chart',
    loadChildren: () =>
      import('./modules/chart.module').then((m) => m.ChartModule),
  },
  {
    path: 'heat-index',
    loadChildren: () =>
      import('./modules/heat-index.module').then((m) => m.HeatIndexModule),
  },
  {
    path: '**',
    component: PageNotFoundComponent,
  },
];

@NgModule({
  declarations: [],
  imports: [CommonModule, RouterModule.forRoot(appRoutes)],
})
export class AppRoutingModule {}

已尝试的无效方法

  • 改用ngAfterViewInit钩子初始化数据
  • 在订阅回调中添加setTimeout延迟赋值
  • 提前初始化basicData对象

解决方案

方案一:手动触发图表重绘

通过ViewChild获取Chart组件实例,在数据更新后调用refresh()方法强制重绘:

  1. 在组件中引入并获取Chart实例
import { Chart } from 'primeng/chart';
import { ViewChild } from '@angular/core';

export class ChartComponent implements OnInit, OnDestroy {
  // ...其他代码
  @ViewChild(Chart) chart: Chart;

  // ...订阅回调中添加
  next: (data) => {
    this.basicData.labels = data.labels.map((time) =>
      this.datePipe.transform(time, 'shortTime')
    );
    this.basicData.datasets = data.datasets;
    // 手动触发重绘
    if (this.chart) {
      this.chart.refresh();
    }
  },
}

方案二:创建新的basicData对象触发变更检测

不修改原有对象的属性,而是直接赋值新的对象,让Angular感知到引用变化:

next: (data) => {
  this.basicData = {
    labels: data.labels.map((time) => this.datePipe.transform(time, 'shortTime')),
    datasets: [...data.datasets] // 浅拷贝数组确保引用更新
  };
},

方案三:在Service中完成日期格式化(推荐)

将DatePipe注入到Service中,直接返回格式化后的数据,组件直接赋值整个对象:

  1. 在Service中注入DatePipe(需在模块中提供DatePipe)
import { DatePipe } from '@angular/common';

@Injectable({ providedIn: 'root' })
export class WeatherService {
  constructor(private http: HttpClient, private datePipe: DatePipe) {}

  // ...修改getWeatherDataForChart方法
  getWeatherDataForChart(): Observable<{
      labels: string[];
      datasets: Dataset[];
    }> {
      return this.getWeatherData().pipe(
        map((historicalWeatherData) => {
          const data = historicalWeatherData.hourly.temperature_2m.map(
            (value, i) => {
              return {
                temperature: value,
                time: historicalWeatherData.hourly.time[i],
              };
            }
          );
          // 在Service中完成日期格式化
          const labels = data.map((item) => this.datePipe.transform(item.time, 'shortTime'));
          const datasets = [
            new Dataset(
              'Temperature',
              data.map((item) => item.temperature),
              false,
              '#42A5F5',
              0.4
            ),
          ];
          return { labels, datasets };
        })
      );
    }
}
  1. 组件中直接赋值
next: (data) => {
  this.basicData = data;
},

问题根源

当仅修改basicData的嵌套属性(labels/datasets)时,Angular变更检测虽能感知内容变化,但PrimeNG Chart内部未监听这些嵌套属性的更新,仅当data输入属性的引用发生变化,或窗口resize触发Chart.js的重绘逻辑时,图表才会渲染。

内容的提问来源于stack exchange,提问作者krk5570

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.04 21:35:27