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()方法强制重绘:
- 在组件中引入并获取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中,直接返回格式化后的数据,组件直接赋值整个对象:
- 在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 }; }) ); } }
- 组件中直接赋值
next: (data) => { this.basicData = data; },
问题根源
当仅修改basicData的嵌套属性(labels/datasets)时,Angular变更检测虽能感知内容变化,但PrimeNG Chart内部未监听这些嵌套属性的更新,仅当data输入属性的引用发生变化,或窗口resize触发Chart.js的重绘逻辑时,图表才会渲染。
内容的提问来源于stack exchange,提问作者krk5570
相关产品推荐
相关产品推荐

