Angular - Higchart doesn´t wait to ngOnInit

Viewed 580

I created a Highchart with custom range filter. The chart data is loaded from a web service. I call this service from ngOnInit method. Like this

TS

import { Component, OnInit } from "@angular/core";
import * as Highcharts from "highcharts/highstock";
import { HttpClient } from "@angular/common/http";
import * as moment from "moment";
declare var require: any;
require("highcharts/highcharts-more")(Highcharts);
require("highcharts/modules/exporting")(Highcharts);
require("highcharts/modules/export-data")(Highcharts);

@Component({
  selector: "app-root",
  templateUrl: "./app.component.html"
})
export class AppComponent implements OnInit {
  seriesOptions: any = [];
  ranges: any;
  pickerlang: any;
  seriesCounter = 0;
  names = ["MSFT", "AAPL"];
  selected: any;
  chart: any;
  updateFromInput = false;
  Highcharts = Highcharts;
  chartConstructor = "stockChart";
  chartCallback: any;
  chartOptions: any;
  constructor(private http: HttpClient) {
    const self = this;

    this.chartCallback = chart => {
      self.chart = chart;
    };
  }

  ngOnInit() {
    moment.locale("es");
    this.names.forEach((element, i) => {
      this.http
        .get(
          "https://www.highcharts.com/samples/data/" +
            element.toLowerCase() +
            "-c.json"
        )
        .forEach((data: any) => {
          this.seriesOptions[i] = {
            name: element,
            data: data
          };
          this.seriesCounter += 1;
          if (this.seriesCounter === this.names.length) {
            this.createGraph();
          }
        });
    });

    this.ranges = {
      Hoy: [moment(), moment()],
      Ayer: [moment().subtract(1, "days"), moment().subtract(1, "days")],
      "Últimos 7 días": [moment().subtract(6, "days"), moment()],
      "Últimos 30 días": [moment().subtract(29, "days"), moment()],
      "Este mes": [moment().startOf("month"), moment().endOf("month")],
      "Mes pasado": [
        moment()
          .subtract(1, "month")
          .startOf("month"),
        moment()
          .subtract(1, "month")
          .endOf("month")
      ]
    };

    this.pickerlang = {
      format: "DD/MM/YYYY",
      direction: "ltr",
      weekLabel: "W",
      cancelLabel: "Cancelar",
      applyLabel: "Aceptar",
      clearLabel: "",
      daysOfWeek: moment.weekdaysMin(),
      monthNames: moment.monthsShort(),
      customRangeLabel: "Seleccionar fecha",
      firstDay: 1
    };
  }
  createGraph() {
    this.chartOptions = {
      lang: {
        loading: "Cargando...",
        months: [
          "Enero",
          "Febrero",
          "Marzo",
          "Abril",
          "Mayo",
          "Junio",
          "Julio",
          "Agosto",
          "Septiembre",
          "Octubre",
          "Noviembre",
          "Diciembre"
        ],
        weekdays: [
          "Domingo",
          "Lunes",
          "Martes",
          "Miércoles",
          "Jueves",
          "Viernes",
          "Sábado"
        ],
        shortMonths: [
          "Ene",
          "Feb",
          "Mar",
          "Abr",
          "May",
          "Jun",
          "Jul",
          "Ago",
          "Sep",
          "Oct",
          "Nov",
          "Dic"
        ],
        rangeSelectorFrom: "Desde",
        rangeSelectorTo: "Hasta",
        rangeSelectorZoom: "Período",
        downloadPNG: "Descargar imagen PNG",
        downloadJPEG: "Descargar imagen JPEG",
        downloadPDF: "Descargar imagen PDF",
        downloadSVG: "Descargar imagen SVG",
        downloadCSV: "Descargar imagen CSV",
        downloadXLS: "Descargar imagen XLS",
        printChart: "Imprimir",
        resetZoom: "Reiniciar zoom",
        resetZoomTitle: "Reiniciar zoom",
        viewData: "Ver tabla",
        openInCloud: "Ver en web",
        thousandsSep: ",",
        decimalPoint: "."
      },
      xAxis: {
        crosshair: {
          width: 1,
          color: "red"
        }
      },
      rangeSelector: {
        enabled: false
      },

      chart: {
        zoomType: "x",
        panning: true,
        panKey: "shift"
      },
      exporting: {
        enabled: true
      },

      plotOptions: {
        series: {
          showInNavigator: true
        }
      },
      tooltip: {
        pointFormat:
          '<span style="color:{series.color}">{series.name}</span>: <b>{point.y} litros</b><br/>',
        valueDecimals: 2,
        split: true
      },
      series: this.seriesOptions
    };
  }

  setExtremes() {
    const self = this,
      chart = self.chart;

    chart.showLoading();
    setTimeout(() => {
      chart.hideLoading();

      chart.xAxis[0].setExtremes(
        new Date(
          this.selected["startDate"].toJSON().substring(0, 10)
        ).getTime(),
        new Date(this.selected["startDate"].toJSON().substring(0, 10)).getTime()
      );
      self.updateFromInput = true;
    }, 1000);
  }
}

HTML

 <div class="card">
    <div class="card-body">
      <input type="text" ngxDaterangepickerMd [locale]="pickerlang" [(ngModel)]="selected" class="form-control"
        placeholder="Rango de fecha" class="form-control" [ranges]="ranges" [showCustomRangeLabel]="true"
        [alwaysShowCalendars]="true" />
      <highcharts-chart id="container" [Highcharts]="Highcharts" [constructorType]="chartConstructor"
        [options]="chartOptions" [callbackFunction]="chartCallback" [(update)]="updateFromInput" [oneToOne]="true">
      </highcharts-chart>
      <button (click)=" setExtremes()" class="btn btn-primary">Buscar </button>
    </div>
  </div>

Chart is trying to load data before is start ngOninit, so return in console.log

Cannot read property 'series' of undefined

Anyway all works, chart has data and filter is fine, but i want chart wait to ngOnit to show data to avoid show this error in console.

So the question is: How can i force highchart to wait to ngOnInit?

SOLUTION

Full solution code: https://github.com/ElHombreSinNombre/angular-highcharts

2 Answers

This by trying to create a chart with undefined options. If the options will be loaded dynamically later then you could use an empty object or add the chart component when the data will be loaded.

Refer the issue for more details.

I am just guessing that Highcharts is the one that has series property.

<highcharts-chart id="container" [Highcharts]="Highcharts ? Highcharts : []" [constructorType]="chartConstructor"
        [options]="chartOptions" [callbackFunction]="chartCallback" [(update)]="updateFromInput" [oneToOne]="true">
</highcharts-chart>
Related