简体   繁体   English

以角度动态更新 Chart.js 数据

[英]Dynamically updating Chart.js data in angular

I"m attempting to update my chart data, specifically the labels for the x-axis, dynamically based on the data returned from an api call. I have a component called 'dashboard-card-component' defined like:我正在尝试根据从 api 调用返回的数据动态更新我的图表数据,特别是 x 轴的标签。我有一个名为“dashboard-card-component”的组件,其定义如下:

import { Component, OnInit, AfterViewInit, Input, ViewChild, Injectable } from '@angular/core';
import { BaseChartDirective, Label, Color } from 'ng2-charts';
import { ChartDataSets, ChartOptions } from 'chart.js';
import * as pluginAnnotations from 'chartjs-plugin-annotation';
import { DailyStats } from '../../general-stats/general-stats.component';

// mocked data is an any
/*{
  title: 'BlockChain Reciepts by Day', type: 'line',
  value: {
    data: [65, 59, 80, 81, 56, 55, 40], label: 'Number Receipts'
  }, cols: 1, rows: 1
}*/

@Component({
  selector: 'app-dashboard-card',
  templateUrl: './dashboard-card.component.html',
  styleUrls: ['./dashboard-card.component.scss']
})

@Injectable({
  providedIn: 'root'
})

export class DashboardCardComponent implements OnInit, AfterViewInit {
  public lineChartData: ChartDataSets[] = [];
  public lineChartLabels: Label[] = ['January', 'February', 'March', 'April', 'May', 'June', 'July'];

  public lineChartOptions: (ChartOptions & { annotation: any }) = {
    responsive: true,
    scales: {
      // We use this empty structure as a placeholder for dynamic theming.
      xAxes: [{}],
      yAxes: [
        {
          id: 'y-axis-0',
          position: 'left',
        }/*,
        {
          id: 'y-axis-1',
          position: 'right',
          gridLines: {
            color: 'rgba(255,0,0,0.3)',
          },
          ticks: {
            fontColor: 'red',
          }
        }*/
      ]
    },
    annotation: {
      annotations: [/*
        {
          type: 'line',
          mode: 'vertical',
          scaleID: 'x-axis-0',
          value: 'March',
          borderColor: 'orange',
          borderWidth: 2,
          label: {
            enabled: true,
            fontColor: 'orange',
            content: 'LineAnno'
          }
        },
      */],
    },
  };
  public lineChartColors: Color[] = [
    { // dark grey
      backgroundColor: 'rgba(77,83,96,0.2)',
      borderColor: 'rgba(77,83,96,1)',
      pointBackgroundColor: 'rgba(77,83,96,1)',
      pointBorderColor: '#fff',
      pointHoverBackgroundColor: '#fff',
      pointHoverBorderColor: 'rgba(77,83,96,1)'
    }
  ];
  public lineChartLegend = false;
  public lineChartType = 'line';
  public lineChartPlugins = [pluginAnnotations];

  @ViewChild(BaseChartDirective, { static: false }) chart: BaseChartDirective;

  @Input() card: any;

  constructor() { }

  ngOnInit() {
    if (this.card.type === 'line') {
      this.lineChartData.push(this.card.value);
    }
  }

  ngAfterViewInit() {
    console.log(this.chart);
  }

  public refreshChart(data: any) {
    console.log(`data is: ${data}`); // I set a breakpoint here in the debugger. this.chart isn't defined
    setTimeout(() => {
      if (this.chart && this.chart.chart && this.chart.chart.config) {
        this.chart.chart.config.data.labels = data;
        this.chart.chart.update();  // chart won't update because this.chart is undefined
      }
    });
  }

  // events
  public chartClicked({ event, active }: { event: MouseEvent, active: {}[] }): void {
    // console.log(event, active);
  }

  public chartHovered({ event, active }: { event: MouseEvent, active: {}[] }): void {
    // console.log(event, active);
  }

}

I have a general-stats-component that calls out to an api to retrieve the data to update the labels for the chart and re-rendering it:我有一个通用统计组件,它调用一个 api 来检索数据以更新图表的标签并重新呈现它:

import { Component, OnInit } from '@angular/core';
import { StatsService, PenStats } from '../services/stats.service';
import { DashboardCardComponent } from '../common/dashboard-card/dashboard-card.component';

export interface PenMetrics {
  users: number;
  logins: number;
  newSignups: number;
}

export interface DailyStats {
  createdAt: string;
  date: string;
  leakedPasswords: number;
  logins: number;
  signups: number;
  updatedAt: string;
}

@Component({
  selector: 'app-general-stats',
  templateUrl: './general-stats.component.html',
  styleUrls: ['./general-stats.component.scss']
})

export class GeneralStatsComponent implements OnInit {
  stats: PenStats;
  displayedColumns: string[] = ['users', 'logins', 'newSignups'];
  dataSource: PenMetrics[];
  users: object;
  dailyStats: any = [];
  tempStats: object;
  userCount: number;
  logins: number;
  newSignups: number;
  labelsArr: any = [];

  cards = [
    {
      title: 'Unique Pen Logins Per Day', type: 'line',
      value: {
        data: [], label: 'Pen Logins'
      }, cols: 1, rows: 1
    },
    {
      title: 'Pen Signatures by Month', type: 'line',
      value: {
        data: [20, 39, 80, 16, 44, 18, 80], label: 'Pen Signatures'
      }, cols: 1, rows: 1
    },
    {
      title: 'BlockChain Reciepts Written by Month', type: 'line',
      value: {
        data: [65, 59, 80, 81, 56, 55, 40], label: 'Number Receipts'
      }, cols: 1, rows: 1
    }
    ,
    {
      title: 'BlockChain Validations by Month', type: 'line',
      value: {
        data: [165, 159, 80, 181, 156, 155, 240], label: 'Number Validations'
      }, cols: 1, rows: 1
    }
  ];

  constructor(
    private statsService: StatsService,
    private dashboardComponent: DashboardCardComponent
  ) { }

  ngOnInit() {
    this.dataSource = this.statsService.getPenStats();
    this.statsService.getPenUsers().subscribe(data => {
      this.users = data;
      this.userCount = Object.keys(this.users).length;
      console.log(this.users);
    });
    this.dashboardComponent.lineChartLabels.length = 0;

    // const currentStats: DailyStats[] = [];
    this.statsService.getDailyStats().subscribe(stats => {
      for (const d of (stats as any)) {
        this.dailyStats.push({
          date: d.date,
          logins: d.logins
        });
        this.dashboardComponent.lineChartLabels.push(d.date);
        console.log(`date is:  ${d.date}`);
        this.cards[0].value.data.push(d.logins);
      }
      this.labelsArr = this.dashboardComponent.lineChartLabels;
      this.dashboardComponent.lineChartLabels.forEach(label => {
        console.log(`label is: ${label}`);
      });
      this.dashboardComponent.refreshChart(this.labelsArr);
      console.log(this.dailyStats);
    });
  }
}

The call out to:呼吁:

this.dataSource = this.statsService.getPenStats();

returns a valid json string in the form:以以下形式返回有效的 json 字符串:

[
  {
    "date": "2020-08-12T00:00:00.000Z",
    "created_at": "2020-08-12T16:22:27.361Z",
    "leaked_passwords": 0,
    "logins": 1,
    "signups": 0,
    "updated_at": "2020-08-12T23:22:11.660Z"
  },
  {
    "date": "2020-08-13T00:00:00.000Z",
    "created_at": "2020-08-13T19:22:38.695Z",
    "leaked_passwords": 0,
    "logins": 1,
    "signups": 1,
    "updated_at": "2020-08-13T23:22:28.062Z"
  },
  {
    "date": "2020-08-14T00:00:00.000Z",
    "created_at": "2020-08-14T17:22:47.806Z",
    "leaked_passwords": 0,
    "logins": 2,
    "signups": 0,
    "updated_at": "2020-08-14T23:22:08.882Z"
  },
  {
    "date": "2020-08-17T00:00:00.000Z",
    "created_at": "2020-08-17T15:22:13.676Z",
    "leaked_passwords": 0,
    "logins": 10,
    "signups": 4,
    "updated_at": "2020-08-17T23:22:10.597Z"
  },
  {
    "date": "2020-08-18T00:00:00.000Z",
    "created_at": "2020-08-18T13:21:57.014Z",
    "leaked_passwords": 0,
    "logins": 8,
    "signups": 3,
    "updated_at": "2020-08-18T23:22:36.734Z"
  },
  {
    "date": "2020-08-19T00:00:00.000Z",
    "created_at": "2020-08-19T13:22:01.569Z",
    "leaked_passwords": 0,
    "logins": 3,
    "signups": 1,
    "updated_at": "2020-08-19T23:22:17.038Z"
  },
  {
    "date": "2020-08-20T00:00:00.000Z",
    "created_at": "2020-08-20T13:22:14.392Z",
    "leaked_passwords": 0,
    "logins": 27,
    "signups": 15,
    "updated_at": "2020-08-20T23:22:17.330Z"
  },
  {
    "date": "2020-08-21T00:00:00.000Z",
    "created_at": "2020-08-21T01:21:25.918Z",
    "leaked_passwords": 0,
    "logins": 7,
    "signups": 5,
    "updated_at": "2020-08-21T23:22:16.300Z"
  },
  {
    "date": "2020-08-23T00:00:00.000Z",
    "created_at": "2020-08-23T20:21:36.804Z",
    "leaked_passwords": 0,
    "logins": 3,
    "signups": 3,
    "updated_at": "2020-08-23T23:21:38.221Z"
  },
]

How can I get access to my chart in order to update it once I've retrieved the data.我怎样才能访问我的图表以便在我检索到数据后更新它。 I check the value of this.dashboardComponent.lineChartLabels and it's correctly populated with the dates I'd like to use as labels on the x-axis, but I can't get it to update when calling this.dashboardComponent.refreshChart(this.labelsArr);我检查了this.dashboardComponent.lineChartLabels的值,它正确填充了我想用作 x 轴标签的日期,但是在调用this.dashboardComponent.refreshChart(this.labelsArr);

DashboardCardComponent.ts DashboardCardComponent.ts

 //@Input() card: any;
 @Input() card$: Observable<any>;
 

tap the observable to fetch data and subsequent changes点击 observable 以获取数据和后续更改

ngOnInit() {
//if (this.card.type === 'line') {
//  this.lineChartData.push(this.card.value);
//}
this.card$.pipe(
 tap(data=>this.lineChartData.push(data.value))
 .subscribe() // either unsubscribe in onDestroy or use async pipe in html
 }

In your calling component GeneralStatsComponent在您的调用组件 GeneralStatsComponent

Subject card = new Subject() // from rsjx
card.next( ... data... ) // data fetched from api or your selection logic 

GeneralStatsComponent.html通用统计组件.html

<app-dashboard-card [card$]='card' ...>

Above is just a pseudo code .. syntax needs to be reviewed.以上只是一个伪代码..语法需要审查。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM