12

I am using Doughnut chart from ng2-charts (http://valor-software.com/ng2-charts/) in angular 2. I have been searching for an option to put a text in the middle without success. I already searched in ng-chart options as in chart.js (dependency). Do you know another way to achieve this in Angular 2 typescript? or there is something I am missing?

Ivan Juarez
  • 1,413
  • 4
  • 21
  • 30

3 Answers3

7

You can place both LABEL and its Value in center of Doughnut.

When you hover it hover value will be updated in the center of chart.

import { Component, OnInit } from '@angular/core';
import { ChartType } from 'chart.js';
import { SingleDataSet, Label, PluginServiceGlobalRegistrationAndOptions } from 'ng2-charts';

@Component({
  selector: 'my-app',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent {
  // Doughnut
  public doughnutChartLabels = ['Download Sales', 'In-Store Sales'];
  public doughnutChartData: SingleDataSet = [
    [350, 450]
  ];
  public doughnutChartType: ChartType = 'doughnut';
  public doughnutChartPlugins: PluginServiceGlobalRegistrationAndOptions[] = [{
    afterDraw(chart) {
      const ctx = chart.ctx;
      var txt1 = '';
      var txt2 = '';    

      try{
        var check = chart.active ? chart.tooltip._active[0]._datasetIndex : "None";
        if(check !== "None"){
        txt1 = chart.tooltip._data.labels[chart.tooltip._active[0]._index];
        txt2 = chart.tooltip._data.datasets[0].data[chart.tooltip._active[0]._index];        
      }else{
        txt1 = chart.tooltip._data.labels[0];
        txt2 = chart.tooltip._data.datasets[0].data[0];
      }
      }
      catch(err){
        txt1 = chart.tooltip._data.labels[0] 
        txt2 = chart.tooltip._data.datasets[0].data[0];
      }
      //Get options from the center object in options
      const sidePadding = 60;
      const sidePaddingCalculated = (sidePadding / 100) * (chart.innerRadius * 2)

      ctx.textAlign = 'center';
      ctx.textBaseline = 'middle';
      const centerX = ((chart.chartArea.left + chart.chartArea.right) / 2);
      const centerY = ((chart.chartArea.top + chart.chartArea.bottom) / 2);

      //Get the width of the string and also the width of the element minus 10 to give it 5px side padding

      const stringWidth = ctx.measureText(txt1).width;
      const elementWidth = (chart.innerRadius * 2) - sidePaddingCalculated;

      // Find out how much the font can grow in width.
      const widthRatio = elementWidth / stringWidth;
      const newFontSize = Math.floor(30 * widthRatio);
      const elementHeight = (chart.innerRadius * 2);

      // Pick a new font size so it will not be larger than the height of label.
      const fontSizeToUse = 30;
      ctx.font = fontSizeToUse + 'px Arial';
      ctx.fillStyle = 'black';

      // Draw text in center
      ctx.fillText(txt2, centerX, centerY - 10);
      var fontSizeToUse1 = 15;
      ctx.font = fontSizeToUse1 + 'px Arial';
      ctx.fillText(txt1, centerX, centerY + 10);
    }
  }];

  constructor() { }

  ngOnInit() {
  }

  // events
  public chartClicked({ event, active }: { event: MouseEvent, active: {}[] }): void {
    //console.log(event, active);
  }

  public chartHovered({ event, active }: { event: MouseEvent, active: {}[] }): void {
    //console.log(event, active);
  }
}

HTML

<div>
  <div>
    <div style="display: block">
      <canvas baseChart [data]="doughnutChartData" [labels]="doughnutChartLabels" [chartType]="doughnutChartType"
        [plugins]="doughnutChartPlugins" (chartHover)="chartHovered($event)"
        (chartClick)="chartClicked($event)"></canvas>
    </div>
  </div>
</div>

enter image description here

Happy Coding :)

Ramkee
  • 900
  • 1
  • 10
  • 27
6

You can do the following to place text in the center of doughnut chart. It worked for me

HTML:

 <div style="display: block">
  <canvas #mycanvas baseChart 
              [data]="doughnutChartData"
              [labels]="doughnutChartLabels"
              [chartType]="doughnutChartType"
              (chartHover)="chartHovered($event)"
              (chartClick)="chartClicked($event)"></canvas>
</div>

Typescript

import {Component, NgModule, ElementRef, Inject, ViewChild} from '@angular/core'
import {BrowserModule} from '@angular/platform-browser'
import {ChartsModule, Color} from 'ng2-charts';

export class App{
   @ViewChild('mycanvas')
  canvas:ElementRef; 

 ngOnInit(){
    var ctx = this.canvas.nativeElement.getContext("2d");

    let me = this;
    this.options = {

      circumference: Math.PI,
      rotation :  Math.PI,
      animation:{ onComplete: function() {
         me.doit(ctx);
       }}
    }

      }

     doit(ctx) {
         //   Chart.types.Doughnut.prototype.draw.apply(this, arguments);

            var width = this.canvas.nativeElement.clientWidth,
                height = this.canvas.nativeElement.clientHeight;

            var fontSize = (height / 250).toFixed(2);
            ctx.font = fontSize + "em Verdana";
            ctx.textBaseline = "middle";
            ctx.fillStyle = "blue";

            var text = "Pass Rate 82%",
                textX = Math.round((width - ctx.measureText(text).width) / 2),
                textY = height -10;

            ctx.fillText(text, textX, textY);
            ctx.restore();
        }
}
}
powerranger
  • 133
  • 2
  • 8
  • 2
    Awesome! This works for me, I just had to place the text inside the chart and in the middle of it.. So the ´textY´ variable should be: ´textY = height / 2´. Also, it seems like everytime the mouse hovers over the chart, the function ´doit´ is called, so the text disappears and appears again. So, you should do the next in order to fix it.. https://stackoverflow.com/a/43286397/2621320 – Gerardo Tarragona Aug 14 '17 at 22:06
  • Or you can use animation.onProgress and call the same 'doit' function in it. – antony Jun 06 '19 at 06:40
0
import { ChartType, ChartOptions } from 'chart.js';
import { MultiDataSet, Label, PluginServiceGlobalRegistrationAndOptions } from 'ng2-charts';

that: this;
inValue: string = 'TEXT you need to add in dynamicly';

  public doughnutChartLabels: Label[] = [];
  public doughnutChartData: MultiDataSet = [];
  public doughnutChartPlugins: PluginServiceGlobalRegistrationAndOptions[]=[{}];
  public doughnutChartType: ChartType = 'doughnut';
  public config: ChartOptions = {
    responsive: true,
    maintainAspectRatio: false,
    legend: {
      position: 'bottom'
    },
    tooltips: {
      enabled: false
    },
    rotation: 0,
    cutoutPercentage: 65
  };
  colors = [
    {
      backgroundColor: [
        '#63ba68',
        '#eae9e9'
      ],
      borderWidth: 0
    }
  ];
  ngOnInit(): void {
    this.doughnutChartData.push([[[your data]]]);
  }

  ngAfterViewInit(): void {
    this.that = this;
    this.doughnutChartPlugins= [this.returnObjectDoughnutChartPlugins(this.that)]
  }

  returnObjectDoughnutChartPlugins(that: this) {
    return {
      beforeDraw(chart) {
        const ctx = chart.ctx;
        const txt: string = that.inValue;
        ctx.textAlign = 'center';
        ctx.textBaseline = 'middle';
        const centerX = ((chart.chartArea.left + chart.chartArea.right) / 2);
        const centerY = ((chart.chartArea.top + chart.chartArea.bottom) / 2);
        const fontSizeToUse = 22;
        ctx.font = `${fontSizeToUse}px YourFont`;
        ctx.fillStyle = '#444444';
        ctx.fillText(txt, centerX, centerY);
        ctx.shadowColor = '#767676';
        ctx.shadowBlur = 0.5;
        ctx.shadowOffsetX = 0.1;
        ctx.shadowOffsetY = 1;
      }
    }
  }
}
Hamada
  • 1,836
  • 3
  • 13
  • 27