0

I am using amChart5 graph in a component as child component with angular13 and I am sending data from parent component to child component through input decorator it working fine for first-time load but I can't change/update the chart on @Input() decorator data change.

parent component code

 <div class="div3">
    <mat-card class="div3-left shadow"> <app-stacked-bar-chart></app-stacked-bar-chart> </mat-card>
    <mat-card class="div3-right shadow">
        <p style="position:absolute;top: 5px;left :25rem;color: grey;font-style: italic;font-size: small;">Time(24hr
            format)</p>
        <app-heat-map [graphData]="heatMapData" [uniqueVal]="'5'"> </app-heat-map>
        <mat-icon class="btn" data-bs-toggle="modal" data-bs-target="#exampleModal"
            style="position: absolute;right: 1rem;bottom: .5rem;font-weight: 900;color: grey;"> fullscreen
        </mat-icon>
    </mat-card>
</div>

child componenet code

 import { Component, Inject, Input, NgZone, OnChanges, OnInit, 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/Material';

@Component({
  selector: 'app-heat-map',
  templateUrl: './heat-map.component.html',
  styleUrls: ['./heat-map.component.css']
})
export class HeatMapComponent implements OnInit,OnChanges {
  @Input() uniqueVal: any="5"
  @Input() graphData: any = []

  graphData2:any


  private root: am5.Root | undefined;

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

  ngOnInit(): void {
   
  }

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

  ngOnChanges() {
    function dateToDay(data: any) {
      var dayArr: any = []
      data.forEach((e: any) => {
        function getDayName(dateStr: any, locale: any) {
          var date = new Date(dateStr);
          return date.toLocaleDateString(locale, { weekday: 'long' });
        }

        let day = getDayName(e.utc_dates, "en-US")

        dayArr.push({ "utc_day": day, "utc_hour": e.utc_hour, "sum_data": e.sum_data })

      })
      return dayArr
    }
    this.graphData2=dateToDay(this.graphData)

    console.log("graphdata2",this.graphData2);  

    /* Chart code */
    let root = am5.Root.new(`chartdiv${this.uniqueVal}`);
    // Set themes
    root.setThemes([
      am5themes_Animated.new(root)
    ]);

    // Create chart
    let chart = root.container.children.push(am5xy.XYChart.new(root, {
      panX: false,
      panY: false,
      wheelX: "none",
      wheelY: "none",
      layout: root.verticalLayout
    }));


    // Create axes and their renderers
    let yRenderer = am5xy.AxisRendererY.new(root, {
      visible: false,
      minGridDistance: 20,
      inversed: true
    });

    yRenderer.grid.template.set("visible", false);

    let yAxis = chart.yAxes.push(am5xy.CategoryAxis.new(root, {
      maxDeviation: 0,
      renderer: yRenderer,
      categoryField: "utc_day"
    }));

    let xRenderer = am5xy.AxisRendererX.new(root, {
      visible: false,
      minGridDistance: 30,
      opposite: true
    });

    xRenderer.grid.template.set("visible", false);

    let xAxis = chart.xAxes.push(am5xy.CategoryAxis.new(root, {
      renderer: xRenderer,
      categoryField: "utc_hour"
    }));

    // Create series
    let series = chart.series.push(am5xy.ColumnSeries.new(root, {
      calculateAggregates: true,
      stroke: am5.color(0xffffff),
      clustered: false,
      xAxis: xAxis,
      yAxis: yAxis,
      categoryXField: "utc_hour",
      categoryYField: "utc_day",
      valueField: "sum_data"
    }));

    series.columns.template.setAll({
      tooltipText: "{sum_data}",
      strokeOpacity: 1,
      strokeWidth: 2,
      width: am5.percent(100),
      height: am5.percent(100)
    });

    series.columns.template.events.on("pointerover", function (event) {
      let di: any = event.target.dataItem;
      if (di) {
        heatLegend.showValue(di.get("value", 0));
      }
    });

    series.events.on("datavalidated", function () {
      heatLegend.set("startValue", series.getPrivate("valueHigh"));
      heatLegend.set("endValue", series.getPrivate("valueLow"));
    });


    // Set up heat rules

    series.set("heatRules", [{
      target: series.columns.template,
      min: am5.color(0xfffb77),
      max: am5.color(0xfe131a),
      dataField: "sum_data",
      key: "fill"
    }]);

    // Add heat legend

    let heatLegend = chart.bottomAxesContainer.children.push(am5.HeatLegend.new(root, {
      orientation: "horizontal",
      endColor: am5.color(0xfffb77),
      startColor: am5.color(0xfe131a)
    }));

    // Set data

    let data = [...dateToDay(this.graphData)]
    console.log("data", data);

    
    series.data.setAll(data)


    yAxis.data.setAll([
      { utc_day: "Sunday" },
      { utc_day: "Monday" },
      { utc_day: "Tuesday" },
      { utc_day: "Wednesday" },
      { utc_day: "Thursday" },
      { utc_day: "Friday" },
      { utc_day: "Saturday" }
    ]);


    xAxis.data.setAll([
      { utc_hour: "0" },
      { utc_hour: "1" },
      { utc_hour: "2" },
      { utc_hour: "3" },
      { utc_hour: "4" },
      { utc_hour: "5" },
      { utc_hour: "6" },
      { utc_hour: "7" },
      { utc_hour: "8" },
      { utc_hour: "9" },
      { utc_hour: "10" },
      { utc_hour: "11" },
      { utc_hour: "12" },
      { utc_hour: "13" },
      { utc_hour: "14" },
      { utc_hour: "15" },
      { utc_hour: "16" },
      { utc_hour: "17" },
      { utc_hour: "18" },
      { utc_hour: "19" },
      { utc_hour: "20" },
      { utc_hour: "21" },
      { utc_hour: "22" },
      { utc_hour: "23" },
      { utc_hour: "24" }

    ]);

    // Make stuff animate on load
    chart.appear(500, 10);
  }

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

  

}
R. Richards
  • 24,603
  • 10
  • 64
  • 64
  • try not to use 'any'. uniqueVal is a 'string' and GraphData for sure has typings defined in its library aswell. OR define your own types. It really helps preventing errors. – Jonathan Pauw Jan 07 '22 at 12:42
  • small hint on [uniqueVal]="'5'". you can just write uniqueVal="5" becasue it is a string literal and therefore does not need to be a binding. – Jonathan Pauw Jan 07 '22 at 12:43

1 Answers1

0

ngOnChanges triggeres on each changes in the binding AND once in the beginning of the creation of the component.

It comes with a parameter of type SimpleChanges. If you use that it will give you autocomplete functionality.

the SimpleChanges object contains properties for every binding you have (in your case 'graphData' and 'uniqueVal') with each having four properties: isFirstChange(), currentValue, previousValue and firstChange.

read more here: https://angular.io/api/core/SimpleChanges

perhaps try:

ngOnChanges(changes: SimpleChanges) {
  if(changes.graphData.currentValue) { // <-- this way it only performs if there is a new input from the binding
    this.updateGraph(changes.graphData.currentValue) // <-- contains the new data
  }
}

updateGraph(graphData) {
  // move all your graph update stuff from the ngOnChanges to here
}
Jonathan Pauw
  • 334
  • 3
  • 7