[Chartjs]-Create multiple dynamic stacked chart using chart.js in Angular 10?

1👍

You can use ViewChild to reference html element and use it inside you component. I have also modified few things in your code to toggle charts.

To summarize:

  1. Use ViewChild to access html element in your component
  2. Updated app component to toggle charts as opposed to just adding data
  3. Updated label to accept click event to toggle checkbox

Take a look at this stackblitz.

app.component.html

<label>
  <input type="checkbox" value=1
(change)="chooseEntity($event.target.checked, 1, entityData[0])">
  Microsoft
</label>
<label>
<input type="checkbox" (change)="chooseEntity($event.target.checked, 2, entityData[1])">
  IBM
</label>

<div *ngIf="chartData.length !== 0">
  <app-limus-utilisation-chart *ngFor="let entity of chartData" [chartdata]="entity"></app-limus-utilisation-chart>
</div>

chart.component.html

<div #chartReport>
    <canvas #canvas></canvas>
</div>

chart.component.ts

import {
  Component,
  ElementRef,
  Input,
  OnInit,
  ViewChild,
  ViewEncapsulation
} from "@angular/core";
import { Chart } from "chart.js";

import { OnChanges } from "@angular/core";
@Component({
  selector: "app-limus-utilisation-chart",
  templateUrl: "./chart.component.html",
  encapsulation: ViewEncapsulation.None
})
export class LimusUtilisationChartComponent implements OnInit, OnChanges {
  myChart: Chart;
  @Input() chartdata: any;
  @ViewChild("canvas") stackchartcanvas: ElementRef;

  constructor() {}
  ngOnChanges() {
    this.getStackedChart();
  }

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

  getStackedChart() {
    Chart.Tooltip.positioners.custom = function(elements, position) {
      //debugger;
      return {
        x: position.x,
        y:
          elements[0]._view.base - (elements[0].height() + elements[1].height())
      };
    };

    const canvas: any = this.stackchartcanvas.nativeElement;
    const ctx = canvas.getContext("2d");
    var data = {
      labels: this.chartdata.buyernames,

      datasets: [
        {
          label: "Utilised Limit",
          data: this.chartdata.utilisedlimitData,
          backgroundColor: "#22aa99"
        },
        {
          label: "Available Limit",
          data: this.chartdata.availablelimit,
          backgroundColor: "#994499"
        }
      ]
    };

    setTimeout(() => {
      this.myChart = new Chart(ctx, {
        type: "bar",
        data: data,
        options: {
          tooltips: {
            mode: "index",
            intersect: true,
            position: "custom",
            yAlign: "bottom"
          },
          scales: {
            xAxes: [
              {
                stacked: true
              }
            ],
            yAxes: [
              {
                stacked: false,
                display: false
              }
            ]
          }
        }
      });
    });
  }
}

4👍

The problem is this line here:

    const canvas: any = document.getElementById('canvas1');

You have multiple elements with that ID on the page (Because you did *ngFor), so it always attaches itself to the first element on the page.

Instead of using getElementByID, you should use Angular’s built-in @ViewChild.

Like this:

chart.component.html:

<canvas #stackchartcanvas></canvas>

chart.component.ts:

@ViewChild("stackchartcanvas") myCanvas: ElementRef<HTMLCanvasElement>;
....
....
....
getStackedChart() {
    const canvas = this.myCanvas.nativeElement;
}

Stackblitz: https://stackblitz.com/edit/angular-chart-js-kny4en?file=src%2Fapp%2Fchart.component.ts

(Also, in your original code, this.chartData.push() ran EVERY time a checkbox was clicked, even if the checkbox was false, but that’s a different, unrelated problem, which has also been fixed.)

Leave a comment