Angular+SignalR图表时间X轴无法获取服务端数据求助
Angular + SignalR + Chart.js 折线图无数据问题修复
问题描述
在Angular项目中通过SignalR实现数据交互,已编写SpeedService服务,能成功从服务端获取时间数据finaltime(页面用<h1>标签可正常显示)。但将该数据作为Chart.js折线图的X轴数据配置后,图表无任何数据显示;替换为具体数值时,图表可正常展示。
相关代码
服务代码(原)
import { Injectable } from '@angular/core'; import * as signalR from '@aspnet/signalr'; import { Chart } from 'chart.js'; import { resolve } from 'dns'; import { BehaviorSubject, Observable } from 'rxjs'; import { ChartDataPm1200 } from '../_models/chartmodel'; export interface ChartData{ data: number, time: number } @Injectable({ providedIn: 'root' }) export class SpeedService { baseUrl = 'https://localhost:7068/'; data: ChartDataPm1200[]; datachart: ChartDataPm1200; resulttime: string; finaltime : any; result:any; private hubConnection: signalR.HubConnection; public StartConnection = () =>{ this.hubConnection = new signalR.HubConnectionBuilder().configureLogging(signalR.LogLevel.Debug) .withUrl("https://localhost:7068/charts", { skipNegotiation: true, transport: signalR.HttpTransportType.WebSockets }) .build(); this.hubConnection.start().then(()=>console.log('Connection started')).catch(err => console.log('Error while connecting: '+err)); } public addTransferChartDataListener = () => { this.hubConnection.on('transferchartdataPm1200', (data) => { this.data= data; console.log(data); }); } public addTransferTime = async() => { this.hubConnection.on('transfertime',(data) => { this.resulttime = JSON.stringify(data).replaceAll('{"time":',''); this.finaltime = this.resulttime.replaceAll('}',''); console.log(this.finaltime); }) return this.finaltime; } }
折线图HTML代码(原)
<h1>{{signalRService.finaltime}}</h1> <!-- for testing purpose --> <div style="display: block;"> <canvas baseChart width= "400px" height= "200px" [data]="linechartdata" [options]="linechartOptions" [legend]="linechartLegend" [type]="'line'"> </canvas> </div>
组件代码(原)
export class LineSpeedChartComponent implements OnInit { public linechartdata:ChartData = { datasets: [{ label:"Ampere", data: [{x:+this.signalRService.finaltime,y:12}] }] }; public linechartOptions: ChartOptions = { responsive: true, scales: { x: { type: 'time', min: Date.now() - (24 * 60 * 60 * 1000), max: Date.now() + (24 * 60 * 60 * 1000), ticks: { source: 'auto', }, time:{ unit: 'hour', //second displayFormats:{ hour: "dd/MM/yyyy HH:mm:ss" //second = 'dd/MM/yyyy HH:mm:ss' }, tooltipFormat: "dd/MM/yyyy HH:mm:ss", }, adapters: { date: { locale: vi, }, }, }, y: { min: 0, }, } }; }
问题原因
- 初始化时机错误:组件的
linechartdata在类初始化时赋值,此时finaltime还未从SignalR获取到数据,初始值为undefined,后续finaltime更新时,linechartdata不会自动同步。 - 非响应式数据:
finaltime是普通变量,Angular变更检测不会自动触发图表更新;且通过字符串替换处理finaltime的方式可能导致数据类型不符合Chart.js时间轴要求(需要数字类型时间戳或Date对象)。 - 异步数据未触发图表更新:SignalR回调更新
finaltime后,没有通知组件重新渲染图表。
修复方案
1. 改造Service,使用BehaviorSubject实现响应式数据
将finaltime改为BehaviorSubject,让组件可以订阅数据变化,同时优化时间数据的解析方式:
import { Injectable } from '@angular/core'; import * as signalR from '@aspnet/signalr'; import { BehaviorSubject } from 'rxjs'; import { ChartDataPm1200 } from '../_models/chartmodel'; export interface ChartData{ data: number, time: number } @Injectable({ providedIn: 'root' }) export class SpeedService { baseUrl = 'https://localhost:7068/'; data: ChartDataPm1200[]; datachart: ChartDataPm1200; // 用BehaviorSubject存储时间数据,初始值设为null private finaltimeSubject = new BehaviorSubject<number | null>(null); // 暴露Observable供组件订阅 finaltime$ = this.finaltimeSubject.asObservable(); private hubConnection: signalR.HubConnection; public StartConnection = () => { this.hubConnection = new signalR.HubConnectionBuilder().configureLogging(signalR.LogLevel.Debug) .withUrl("https://localhost:7068/charts", { skipNegotiation: true, transport: signalR.HttpTransportType.WebSockets }) .build(); this.hubConnection.start().then(() => console.log('Connection started')).catch(err => console.log('Error while connecting: ' + err)); } public addTransferChartDataListener = () => { this.hubConnection.on('transferchartdataPm1200', (data) => { this.data = data; console.log(data); }); } public addTransferTime = () => { this.hubConnection.on('transfertime', (data) => { // 直接解析time字段,转换为数字类型时间戳 const timeValue = Number(data.time); this.finaltimeSubject.next(timeValue); console.log(timeValue); }); } }
2. 改造组件,订阅数据变化并更新图表
在组件中订阅finaltime$,当时间数据更新时同步更新图表数据,并在组件销毁时清理订阅避免内存泄漏:
import { Component, OnInit, OnDestroy } from '@angular/core'; import { ChartData, ChartOptions } from 'chart.js'; import { SpeedService } from '../services/speed.service'; import { Subscription } from 'rxjs'; import vi from 'date-fns/locale/vi'; @Component({ selector: 'app-line-speed-chart', templateUrl: './line-speed-chart.component.html', styleUrls: ['./line-speed-chart.component.css'] }) export class LineSpeedChartComponent implements OnInit, OnDestroy { public linechartdata: ChartData = { datasets: [{ label: "Ampere", data: [] }] }; public linechartOptions: ChartOptions = { responsive: true, scales: { x: { type: 'time', min: Date.now() - (24 * 60 * 60 * 1000), max: Date.now() + (24 * 60 * 60 * 1000), ticks: { source: 'auto', }, time:{ unit: 'hour', displayFormats:{ hour: "dd/MM/yyyy HH:mm:ss" }, tooltipFormat: "dd/MM/yyyy HH:mm:ss", }, adapters: { date: { locale: vi, }, }, }, y: { min: 0, }, } }; private timeSubscription!: Subscription; constructor(public signalRService: SpeedService) {} ngOnInit(): void { // 启动SignalR连接并监听时间数据 this.signalRService.StartConnection(); this.signalRService.addTransferTime(); // 订阅时间数据变化,更新图表 this.timeSubscription = this.signalRService.finaltime$.subscribe(time => { if (time !== null) { // 替换图表数据(若需追加数据可改用push方法) this.linechartdata.datasets[0].data = [{x: time, y: 12}]; } }); } ngOnDestroy(): void { // 销毁订阅,避免内存泄漏 this.timeSubscription.unsubscribe(); } }
3. 更新HTML模板,用async管道订阅Observable
修改<h1>标签的绑定方式,确保能正确显示最新的时间数据:
<h1>{{signalRService.finaltime$ | async}}</h1> <div style="display: block;"> <canvas baseChart width="400px" height="200px" [data]="linechartdata" [options]="linechartOptions" [legend]="linechartLegend" [type]="'line'"> </canvas> </div>
内容的提问来源于stack exchange,提问作者Tín Trương Trí
相关产品推荐
相关产品推荐

