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

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,

        },
      }
  };
}

问题原因

  1. 初始化时机错误:组件的linechartdata在类初始化时赋值,此时finaltime还未从SignalR获取到数据,初始值为undefined,后续finaltime更新时,linechartdata不会自动同步。
  2. 非响应式数据:finaltime是普通变量,Angular变更检测不会自动触发图表更新;且通过字符串替换处理finaltime的方式可能导致数据类型不符合Chart.js时间轴要求(需要数字类型时间戳或Date对象)。
  3. 异步数据未触发图表更新: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í

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.08 07:40:35