ChartsJS issues


#1

Im having some random issues in Ionic 3 with charts JS, they are not dynamically updating data on select box selection, I’m having to double click the legend for it to be drawn, I’m also getting a Skip issue once hovered over or clicked. please see my markup and TS code below.

 <ion-item class="select">
          <ion-label class="tankName">Choose Tank</ion-label>
            <ion-select #T (ionChange)="updateValues(T.value)" [(ngModel)]="Converter.tankSelect">
            <ion-option *ngFor="let item of Converter.tankOptionList" value="{{item.value}}">{{item.text}}</ion-option>         
            </ion-select>
        </ion-item>
  </ion-list> 


  <canvas #lineCanvas class="paramsCharts" width="400" height="400" style="background-color:aliceblue"></canvas>

import { Component, ViewChild } from ‘@angular/core’;
import { NavController, NavParams, ToastController } from ‘ionic-angular’;
//import { Storage } from ‘@ionic/storage’;
//import { AlertController } from ‘ionic-angular’;
import { Chart } from ‘chart.js’;
import ‘chartjs-plugin-annotation’;
import { PopoverController } from ‘ionic-angular’;
//import { TkpAmmoniaPage } from ‘…/tkp-ammonia/tkp-ammonia’;
import { FormGroup, Validators, FormBuilder } from ‘@angular/forms’;
import { Slides } from ‘ionic-angular’;

//pages…///
import { HomePage } from ‘…/…/pages/home/home’;
import { PopoverPage } from ‘…/…/pages/popover/popover’;
import { QrScannerPage } from ‘…/…/pages/qr-scanner/qr-scanner’;
import { SettingsPage } from ‘…/…/pages/settings/settings’;

//Providers//
import { ApiProvider } from ‘…/…/providers/api/api’;
import { Measurement } from ‘…/…/models/measurement’;
import { MeasurementType } from ‘…/…/models/measurementtype’;
import { ConverterProvider } from ‘…/…/providers/converter/converter’;
import { GlobalvarsProvider } from ‘…/…/providers/globalvars/globalvars’;
import { Tank } from ‘…/…/models/tank’;

//Models…//
//import { User } from ‘…/…/models/user’;
//import { Tank } from ‘…/…/models/tank’;

@Component({
selector: ‘page-measurements’,
templateUrl: ‘wap-measurements.html’,

})

export class WapMeasurementsPage {

@ViewChild(‘lineCanvas’) lineCanvas;
@ViewChild(Slides) slides: Slides;

lineChart: any;
chartLabel: any;
statusImg: any;
statusText: any;
dateTimeNow: any;
userID: any;
tanks: Tank[];

public labelArray: string[] = [];
public dataArray: any[] = [];
public upperArray: any[] = [];
public lowerArray: any[] = [];
public desiredArray: any[] = [];

//API Measurements
add_measurement_form: FormGroup;
measurements: Measurement[];
measurementsLimited: Measurement[];
tankID: string;
typeID: string;
latestMeasurement: any;
measurementDifference: any;
measurementType: MeasurementType[];
pageTitle: string;

constructor(
public navCtrl: NavController,
public navParams: NavParams,
//private storage: Storage,
//private alertCtrl: AlertController,
public popoverCtrl: PopoverController,
public apiProvider: ApiProvider,
public Converter: ConverterProvider,
public formBuilder: FormBuilder,
public globalVars: GlobalvarsProvider,
private toast: ToastController)
{

this.statusImg = '';
this.statusText = '';
this.chartLabel = '';
this.tankID = globalVars.getTankID();
this.typeID = globalVars.getCurrentWAPTypeID();
this.dateTimeNow = new Date()
this.dateTimeNow = this.dateTimeNow.toISOString();

this.apiProvider.getMeasurementTypeByID(this.typeID).subscribe(type => {
  this.pageTitle = type[0].Name;
})

//add measurement form builder
// UPDATE THIS WITH TANK DATA MODEL
this.userID = this.globalVars.getUserID();

this.add_measurement_form = formBuilder.group({
    tankID: [this.tankID.toString()],
    reading: ["", Validators.required],
    dateTimeTaken: [this.dateTimeNow.toString()],
    typeID: [this.typeID.toString()]
}); 

this.apiProvider.getUserTanks(this.userID).subscribe(tank => {
  this.tanks = tank;
  //Tank Select Data
  this.Converter.tankOptionList.length = 0;
  for (var i = 0; i < this.tanks.length; i++)
  {
    this.Converter.tankOptionList.push ({ value: i, text: this.tanks[i].Name, checked: false });
  }
})

};

updateValues(value: number){
this.tankID = this.tanks[this.Converter.tankSelect].ID;
this.globalVars.setTankID(this.tanks[this.Converter.tankSelect].ID);
this.updateChart();
}

submitValue(){
//submit value to API…
this.apiProvider.createMeasurement(this.add_measurement_form.value)
.subscribe(
measurement => {
console.log(measurement);
this.updateChart();
},
error => console.log(error)
);
}

ValueSubmitted(){
this.toast.create({
message: ‘You have submitted a value!’ ,
duration: 3000
}).present();
this.navCtrl.push(WapMeasurementsPage);
}

ionViewDidLoad() {
//Measurement API Call code…//

/*
this.storage.get('CurrentTankID').then((val) => {
  this.tankID = val;
})

this.storage.get('CurrentWAPTypeID').then((val) => {
  console.log(val);
  currentType = val;
})
*/

// should we add a default tanbk field to db to sync when online.

this.updateChart();

}

updateChart(){

this.apiProvider.getMeasurementTypeByID(this.typeID).subscribe(measurementtype => {
  this.measurementType = measurementtype;
})

this.apiProvider.getMeasurements(this.tankID, this.typeID).subscribe(measurement => {
  this.measurements = measurement;
  this.latestMeasurement = this.measurements[0].Reading;
  this.measurementDifference = parseFloat(this.measurements[0].Reading) - parseFloat(this.measurements[1].Reading);
  if (this.measurementDifference > 0){
    this.measurementDifference = '+' + this.measurementDifference.toString();
  }
})

this.lineChart = new Chart(this.lineCanvas.nativeElement, {

  type: 'line',
  data: {
    labels: this.labelArray,
    datasets: [
      {  
        label: 'Actual',
        data: this.dataArray,
        duration: 2000,
        easing: 'easeInQuart',
        fill: false,
        borderWidth: 2,
        borderColor: '#46add4'
      },
    /*  {  
        label: 'Upper',
        data: [0,0,0,0,0],
        duration: 2000,
        easing: 'easeInQuart',
        fill: false,
        borderWidth: 2,
        borderColor: 'red',
        radius: 0,
          
      },
      {  
        label: 'Lower',
        data: [0,0,0,0,0],
        duration: 2000,
        easing: 'easeInQuart',
        fill: false,
        borderWidth: 2,
        borderColor: 'red',
        radius: 0,
      },
      {  
        label: 'Desired',
        data: [0,0,0,0,0],
        duration: 2000,
        easing: 'easeInQuart',
        fill: false,
        borderWidth: 2,
        borderColor: 'blue',
        radius: 0,
      }*/
  ] 
},
options: {
  annotation: {
    drawTime: 'afterDatasetsDraw',
    annotations: [{
      type: 'box',
      id: 'upperDangerLevel',
      xScaleID: 'x-axis-0',
      yScaleID:'y-axis-0',
      yMax:1500,
      yMin:1400,
      borderWidth:1,
      backgroundColor:'rgba(244, 72, 66,0.7)',
      onMouseenter: function(e) {},
      onMouseover: function(e) {},
      onMouseleave: function(e) {},
      onMouseout: function(e) {},
      onMousemove: function(e) {},
      onMousedown: function(e) {},
      onMouseup: function(e) {},
      onClick: function(e) {},
      onDblclick: function(e) {},
      onContextmenu: function(e) {},
      onWheel: function(e) {}
    },{
      type: 'box',
      id: 'lowerDangerLevel',
      xScaleID: 'x-axis-0',
      yScaleID:'y-axis-0',
      yMax:1100,
      yMin:1000,
      borderWidth:1,
      backgroundColor:'rgba(244, 72, 66,0.7)',
      onMouseenter: function(e) {},
      onMouseover: function(e) {},
      onMouseleave: function(e) {},
      onMouseout: function(e) {},
      onMousemove: function(e) {},
      onMousedown: function(e) {},
      onMouseup: function(e) {},
      onClick: function(e) {},
      onDblclick: function(e) {},
      onContextmenu: function(e) {},
      onWheel: function(e) {}
    },
    {
      type: 'line',
      id: 'desiredLevel',
      mode: 'horizontal',
      scaleID: 'y-axis-0',
      value: 1350,
      borderWidth: 2,
      borderDash: [2,2],
      borderColor: "green",
      onMouseenter: function(e) {},
      onMouseover: function(e) {},
      onMouseleave: function(e) {},
      onMouseout: function(e) {},
      onMousemove: function(e) {},
      onMousedown: function(e) {},
      onMouseup: function(e) {},
      onClick: function(e) {},
      onDblclick: function(e) {},
      onContextmenu: function(e) {},
      onWheel: function(e) {}
    }]
  },
   title:{
    display: true,
    text:'Level',
  },
  maintainAspectRatio: true,
  legend: {
    display: true,
    labels:{
    useLineStyle: true
    },
    fontSize: 15,
    padding: 0
  },
  scales: {
    yAxes: [{
      ticks:{
        beginAtZero:false,
        stepSize: "auto",
        display: true,
      },
      gridLines: {
        color: 'grey',
        
        display: false
      },
      scaleLabel: {
        display: true,
        labelString: 'Measurement Value (ppt)',
      }
    }],
    xAxes: [{
      ticks:{
        display: true,
        beginAtZero:true,
        stepSize: "auto",
      },
      gridLines: {
        color: 'grey',
        display: false
      },
      scaleLabel: {
        display: true,
        labelString: 'Time',
      }
    }]
  }
}  

})

this.apiProvider.getMeasurementsLimited(this.tankID, this.typeID, '5').subscribe(measurementLimited => {
  
  this.measurementsLimited = measurementLimited;

  // ###### ADD ERROR HANDLING HERE ####
  this.dataArray.length = 0;
  this.upperArray.length = 0;
  this.lowerArray.length = 0;
  this.desiredArray.length = 0;
  this.labelArray.length = 0;

  this.dataArray.push(parseFloat(this.measurementsLimited[0].Reading));
  this.dataArray.push(parseFloat(this.measurementsLimited[1].Reading));
  this.dataArray.push(parseFloat(this.measurementsLimited[2].Reading));
  this.dataArray.push(parseFloat(this.measurementsLimited[3].Reading));
  this.dataArray.push(parseFloat(this.measurementsLimited[4].Reading));

  // Pull in measurement Upper
  /*
  this.dataArray.push(parseFloat(this.measurementsLimited[0].Reading));
  this.dataArray.push(parseFloat(this.measurementsLimited[1].Reading));
  this.dataArray.push(parseFloat(this.measurementsLimited[2].Reading));
  this.dataArray.push(parseFloat(this.measurementsLimited[3].Reading));
  this.dataArray.push(parseFloat(this.measurementsLimited[4].Reading));
  */

  // Pull in measurement Lower
  /*
  this.dataArray.push(parseFloat(this.measurementsLimited[0].Reading));
  this.dataArray.push(parseFloat(this.measurementsLimited[1].Reading));
  this.dataArray.push(parseFloat(this.measurementsLimited[2].Reading));
  this.dataArray.push(parseFloat(this.measurementsLimited[3].Reading));
  this.dataArray.push(parseFloat(this.measurementsLimited[4].Reading));
  */
  
  this.upperArray.push(parseFloat(this.measurementType[0].Upper));
  this.upperArray.push(parseFloat(this.measurementType[0].Upper));
  this.upperArray.push(parseFloat(this.measurementType[0].Upper));
  this.upperArray.push(parseFloat(this.measurementType[0].Upper));
  this.upperArray.push(parseFloat(this.measurementType[0].Upper));

  this.lowerArray.push(parseFloat(this.measurementType[0].Lower));
  this.lowerArray.push(parseFloat(this.measurementType[0].Lower));
  this.lowerArray.push(parseFloat(this.measurementType[0].Lower));
  this.lowerArray.push(parseFloat(this.measurementType[0].Lower));
  this.lowerArray.push(parseFloat(this.measurementType[0].Lower));

  this.desiredArray.push(parseFloat(this.measurementType[0].Desired));
  this.desiredArray.push(parseFloat(this.measurementType[0].Desired));
  this.desiredArray.push(parseFloat(this.measurementType[0].Desired));
  this.desiredArray.push(parseFloat(this.measurementType[0].Desired));
  this.desiredArray.push(parseFloat(this.measurementType[0].Desired));

  // if length of lower limit array = 0, rename upper limit to target, otherwise display lower and upper boundaries.

  this.labelArray.push('');
  this.labelArray.push('');
  this.labelArray.push('');
  this.labelArray.push('');
  this.labelArray.push('');

  if(parseFloat(this.latestMeasurement) === parseFloat(this.measurementType[0].Desired))
  {
    this.statusText = this.measurementType[0].OK;
    this.statusImg = 'https://aquamate.pro/images/app/content/statusOK.ico';
  }
  else if(parseFloat(this.latestMeasurement) < parseFloat(this.measurementType[0].Upper) 
      && parseFloat(this.latestMeasurement) > parseFloat(this.measurementType[0].Lower))
  {
    this.statusText = this.measurementType[0].Warning;
    this.statusImg = 'https://aquamate.pro/images/app/content/statusWarning.ico';
  }
  else if(parseFloat(this.latestMeasurement) > parseFloat(this.measurementType[0].Upper) 
      || parseFloat(this.latestMeasurement) < parseFloat(this.measurementType[0].Lower))
  {
    this.statusText = this.measurementType[0].Danger;
    this.statusImg = 'https://aquamate.pro/images/app/content/statusDanger.ico';
  }
  
  this.lineChart.data.datasets[0].data = this.dataArray;
  this.lineChart.data.datasets[1].data = this.upperArray;
  this.lineChart.data.datasets[2].data = this.lowerArray;
  this.lineChart.data.datasets[3].data = this.desiredArray;
  this.lineChart.data.labels = this.labelArray;
  this.lineChart.update();

})

}
//Navigation stack…///
slideToHistory(){
this.slides.slideTo(1, 500);
}
slideToStatus(){
this.slides.slideTo(0,500);
}
slideToAddVal(){
this.slides.slideTo(2,500);
}
seneyeConnected(){
this.toast.create({
message: ‘Seneye Connection Comming Soon!’ ,
duration: 3000
}).present();
}

returnHome(){
this.navCtrl.push(HomePage);
}

qrScanner(){
this.navCtrl.push(QrScannerPage);
}

showSettingsPage() {
this.navCtrl.push(SettingsPage);
}
presentPopover(myEvent) {
let popover = this.popoverCtrl.create(PopoverPage);
popover.present({
ev: myEvent
});
}

}