2
votes

I am completely new to angular2. My problem is I have created a bar chart through ng2-charts lib and linked it to firebase using angularfire2. I have 2 components and a service that sends and receives data to and from my firebase database. I am able to send data from one component doctor-local.component.tsinto firebase through my data.service.tsand receive it on my doctor-expert.component.ts and keep the data here in sync to changes with values in firebase database and display in real time in this same component, using (ngModelChange) event binding. The bar chart is in this component as well.

Here is my expert.component.ts and doctor-expert.component.html

import {Component} from '@angular/core';
import {DataService} from '../data.service';
import {FirebaseObjectObservable} from 'angularfire2';

@Component({
  selector: 'app-doctor-expert',
  templateUrl: './doctor-expert.component.html',
  styleUrls: ['./doctor-expert.component.css']
})
export class DoctorExpertComponent {
  public items: FirebaseObjectObservable<any>;
  
  public barChartOptions: any = {
    scaleShowVerticalLines: false,
    responsive: true
  };
  public barChartLabels: string[] = ['RBC Count', 'WBC Count', 'Haemoglobin'];
  public barChartType: string = 'bar';
  public barChartLegend: boolean = true;
  rbc: number;
  wbc: number;
  haemo: number;


  public barChartData: any[] = [
    {data: [75, 59, 80], label: 'Current Count'},
    {data: [28, 48, 40], label: 'Average Normal Count'}
  ];

  constructor(private dataService: DataService) {
    this.items = this.dataService.messages;
    this.items.subscribe(data => {
      this.rbc = parseInt((data.rbccount), 10);
      this.wbc = parseInt((data.wbccount), 10);
      this.haemo = parseInt((data.haemocount), 10);
    });
    this.barChartData = [
      {data: [this.rbc, this.wbc, this.haemo], label: 'Current Count'},
      {data: [50, 50, 50], label: 'Average Normal Count'},
    ];
  }

  
  public chartClicked(e: any): void {
    console.log(e);
  }

  public chartHovered(e: any): void {
    console.log(e);
  }
}
<ul class="list-group container">
  <li class="list-group-item">RBC Count: {{(items | async)?.rbccount}} </li>
  <li class="list-group-item">WBC Count: {{(items | async)?.wbccount}} </li>
  <li class="list-group-item">Haemoglobin Count: {{(items | async)?.haemocount}} </li>
</ul>

<div class="container">
  <div style="display: block">
    <canvas baseChart
            [datasets]="barChartData"
            [labels]="barChartLabels"
            [options]="barChartOptions"
            [legend]="barChartLegend"
            [chartType]="barChartType"
            (chartHover)="chartHovered($event)"
            (chartClick)="chartClicked($event)"></canvas>
  </div>
</div>

And here is my data.service.ts

import {Injectable} from '@angular/core';
import 'rxjs/Rx';
import {AngularFire, FirebaseObjectObservable} from 'angularfire2';

@Injectable()
export class DataService {
  public messages: FirebaseObjectObservable<any>;

  constructor( public af: AngularFire ) {
    this.messages = this.af.database.object('data');
  }


  sendData(value1, value2, value3) {
    const message = {
      rbccount: value1,
      wbccount: value2,
      haemocount: value3
    };
    this.messages.update(message);
  }

  sendrbc(value){
    const message = {
      rbccount: value
    };
    this.messages.update(message);
  }

  sendwbc(value2){
    const message = {
      wbccount: value2
    };
    this.messages.update(message);
  }

  sendhaemo(value3){
    const message = {
      haemocount: value3
    };
    this.messages.update(message);
  }
}

"this.items = this.dataService.messages" receives the code from the database and the subscribe method gets the value from the observable. Now I want to update this value received in the barChartData and keep it in sync with the changes in database. So that every time there is a change in data passed through doctor-local.component.ts ,the change occurs in database and the bar chart instantly. I tried doing that in the constructor itself but the data doesn't display in the bar chart at all, let alone updating constantly.

1

1 Answers

8
votes

I did some digging and came up with this very straight forward solution to this. The problem was that the dataset was being loaded asynchronously and the chart was being rendered at the time of init that's why It wasn't able to load the new data arrived.

The work around is to just wait to draw the canvas until your asyncs are finished. In your component:

isDataAvailable:boolean = false; ngOnInit() { asyncFnWithCallback(()=>{ this.isDataAvailable = true}); }

where asyncFnWithCallback() is your function.

And then in your html, wrap your entire chart template with:

<div *ngIf="isDataAvailable"> . . . chart canvas + any other template code . . . </div>

In this case, for doctor-expert.component.ts, the new code looks like this :

import {Component, OnInit} from '@angular/core';
import {DataService} from '../data.service';
import {FirebaseObjectObservable} from 'angularfire2';

@Component({
  selector: 'app-doctor-expert',
  templateUrl: './doctor-expert.component.html',
  styleUrls: ['./doctor-expert.component.css']
})
export class DoctorExpertComponent{
  public items: FirebaseObjectObservable<any>;

  public barChartOptions: any = {
    scaleShowVerticalLines: false,
    responsive: true
  };
  public barChartLabels: string[] = ['RBC Count', 'WBC Count', 'Haemoglobin'];
  public barChartType: string = 'bar';
  public barChartLegend: boolean = true;
  rbc: number;
  wbc: number;
  haemo: number;


  public barChartData: any[] = [];

  isDataAvailable: boolean = false;

  constructor(private dataService: DataService) {
    this.items = this.dataService.messages;
    this.items.subscribe(data => {
      this.rbc = parseInt((data.rbccount), 10);
      this.wbc = parseInt((data.wbccount), 10);
      this.haemo = parseInt((data.haemocount), 10);
      this.barChartData = [
        {data: [this.rbc, this.wbc, this.haemo], label: 'Current Count'},
        {data: [50, 50, 50], label: 'Average Normal Count'},
      ];
      this.isDataAvailable = true;
    });
  }


  public chartClicked(e: any): void {
    console.log(e);
  }

  public chartHovered(e: any): void {
    console.log(e);
  }
}

And the doctor-expert.component.html looks like this:

<ul class="list-group container">
  <li class="list-group-item" (ngModelChanges)="update($event)">RBC Count: {{(items | async)?.rbccount}} </li>
  <li class="list-group-item" (ngModelChanges)="update($event)">WBC Count: {{(items | async)?.wbccount}} </li>
  <li class="list-group-item" (ngModelChanges)="update($event)">Haemoglobin Count: {{(items | async)?.haemocount}} </li>
</ul>

<div class="container" *ngIf="isDataAvailable">
  <div style="display: block">
    <canvas baseChart
            [datasets]="barChartData"
            [labels]="barChartLabels"
            [options]="barChartOptions"
            [legend]="barChartLegend"
            [chartType]="barChartType"
            (chartHover)="chartHovered($event)"
            (chartClick)="chartClicked($event)"></canvas>
  </div>
</div>