1

I am using a chart from AMCharts in an Angular component.

When a data array is input from another component, the data array is empty when accessed inside the AMCharts code. The data array is available to be displayed in the template of the component it is passed into.

This is the first component

import { Component, OnInit, ViewChild } from '@angular/core';
import { ApiService } from '../api.service';
import { NgForm } from '@angular/forms';
import { Logbook } from '../logbook';

@Component({
  selector: 'app-logbook',
  templateUrl: './logbook.component.html',
  styleUrls: ['./logbook.component.css'],
})
export class LogbookComponent implements OnInit {
  displayedColumns: string[] = [
    'date',
    'sitename',
    'noofflights',
    'tflighttime',
    'windspeed',
    'winddirec',
    'comments',
    'actions',
  ];
  dataSource: Logbook[] = [];
  tftime: number = 0;
  noofflights: number = 0;
  noofsites: number = 0;

  constructor(private apiService: ApiService) {}

  ngOnInit(): void {
    this.getflights();
  }

  getflights(): void {
    this.apiService.getFlights().subscribe((result) => {
      this.dataSource = result.sort(
        (a, b) => new Date(b.date).getTime() - new Date(a.date).getTime()
      );
      console.log(this.dataSource)
      this.noofsites = result.length;
      result.forEach((entry) => {
        this.tftime += entry.tflighttime;
        this.noofflights += entry.noofflights;
      });
    });
  }

dataSource is passed to the second component in the first component template

<app-charter [dataSource]="dataSource"></app-charter>

This is the second component with the AMCharts code

import { Component, Input, Inject, NgZone, PLATFORM_ID } from '@angular/core';
import { isPlatformBrowser } from '@angular/common';

// amCharts imports
import * as am5 from '@amcharts/amcharts5';
import * as am5xy from '@amcharts/amcharts5/xy';
import am5themes_Animated from '@amcharts/amcharts5/themes/Animated';
import { Logbook } from '../logbook';

@Component({
  selector: 'app-charter',
  templateUrl: './charter.component.html',
  styleUrls: ['./charter.component.css'],
})
export class CharterComponent {
  private root!: am5.Root;
  @Input()
  dataSource!: Logbook[];

  constructor(
    @Inject(PLATFORM_ID) private platformId: Object,
    private zone: NgZone
  ) {}

  // Run the function only in the browser
  browserOnly(f: () => void) {
    if (isPlatformBrowser(this.platformId)) {
      this.zone.runOutsideAngular(() => {
        f();
      });
    }
  }

  ngAfterViewInit() {
    // Chart code goes in here
    this.browserOnly(() => {
      /* Chart code */
      // Create root element
      // https://www.amcharts.com/docs/v5/getting-started/#Root_element
      let root = am5.Root.new('chartdiv');

      // Set themes
      // https://www.amcharts.com/docs/v5/concepts/themes/
      root.setThemes([am5themes_Animated.new(root)]);

      // Create chart
      // https://www.amcharts.com/docs/v5/charts/xy-chart/
      let chart = root.container.children.push(
        am5xy.XYChart.new(root, {
          panX: true,
          panY: true,
          wheelX: 'panX',
          wheelY: 'zoomX',
          pinchZoomX: true,
        })
      );

      // Add cursor
      // https://www.amcharts.com/docs/v5/charts/xy-chart/cursor/
      let cursor = chart.set('cursor', am5xy.XYCursor.new(root, {}));
      cursor.lineY.set('visible', false);

      // Create axes
      // https://www.amcharts.com/docs/v5/charts/xy-chart/axes/
      let xRenderer = am5xy.AxisRendererX.new(root, { minGridDistance: 30 });
      xRenderer.labels.template.setAll({
        rotation: -90,
        centerY: am5.p50,
        centerX: am5.p100,
        paddingRight: 15,
      });

      let xAxis = chart.xAxes.push(
        am5xy.CategoryAxis.new(root, {
          maxDeviation: 0.3,
          categoryField: 'country',
          renderer: xRenderer,
          tooltip: am5.Tooltip.new(root, {}),
        })
      );

      let yAxis = chart.yAxes.push(
        am5xy.ValueAxis.new(root, {
          maxDeviation: 0.3,
          renderer: am5xy.AxisRendererY.new(root, {}),
        })
      );

      // Create series
      // https://www.amcharts.com/docs/v5/charts/xy-chart/series/
      let series = chart.series.push(
        am5xy.ColumnSeries.new(root, {
          name: 'Series 1',
          xAxis: xAxis,
          yAxis: yAxis,
          valueYField: 'value',
          sequencedInterpolation: true,
          categoryXField: 'country',
          tooltip: am5.Tooltip.new(root, {
            labelText: '{valueY}',
          }),
        })
      );

      series.columns.template.setAll({ cornerRadiusTL: 5, cornerRadiusTR: 5 });

      let populateddata: any[] = [];
      console.log(this.dataSource);
      this.dataSource.forEach((data) => {
        populateddata.push({
          country: data.date,
          value: data.tflighttime,
        });
      });

      // Set data
      let data = populateddata;

      xAxis.data.setAll(data);
      series.data.setAll(data);

      // Make stuff animate on load
      // https://www.amcharts.com/docs/v5/concepts/animations/
      series.appear(1000);
      chart.appear(1000, 100);
    });
  }

  ngOnDestroy() {
    // Clean up chart when the component is removed
    this.browserOnly(() => {
      if (this.root) {
        this.root.dispose();
      }
    });
  }
}

This is the second component template

<div id="chartdiv" style="width: 100%; height: 500px"></div>
<div class="">{{ dataSource }}</div>
<li *ngFor="let data of dataSource">
    {{data.date | date: 'dd/MM/yyyy' }}
    {{data.tflighttime}}
</li>

The data in the dataSource array is available in the second component template. The dataSource array is empty when accessed within the AMCharts code.

Can anyone identify why the dataSource array is empty when accessed within the AMCharts code? Could the issue be related to the data coming from an API?

I tried the same set up with hardcoded data and the array was not empty when accessed by the AMCharts code (the data source when the error occurs is from an API).

AMSort
  • 11
  • 3

1 Answers1

0

Got a FIX by adding ngOnchange(){} and declare all the chart stuff in a functionA

, call that functionA in onchange when

if(this.datasource) {
// call `functionA`
}
Ram
  • 65
  • 8