Ngx-charts: Show percentage on Pie Chart

Created on 12 Oct 2018  路  19Comments  路  Source: swimlane/ngx-charts

I'm submitting a ... (check one with "x")

  • [ ] bug report - search github for a similar issue or PR before submitting
  • [x] feature request
  • [ ] support request - use StackOverflow (add the ngx-charts tag) or the gitter chat for support questions

Current behavior
The pie chart only shows colour for each segment, would be great to present a percentage label within the segment. (like in #687)

What is the motivation / use case for changing the behavior?
Better illustration

Most helpful comment

My children, please add this to your angular code to add percentages to the labelling:

setTimeout(function(){
    let labels = $('.pie-label');
    for (let label of labels){
      label.innerHTML += "%";
    }
}, 100)

It's not ideal, but it works.
You are welcome.

All 19 comments

I've made a hack, it will draw the percentage based on each slice's position.
I've never played with svg before, not sure if it's correct way to do it. At least it works:)
Here is the code:

private drawOnPieChart() {
    // get the ngx chart element
    let node = this.basicPieChart.chartElement.nativeElement;
    let svg;
    for (let i = 0; i < 5; i++) {
      if (i === 3) {
        // this is the pie chart svg
        svg = node.childNodes[0];
      }
      // at the end of this loop, the node should contain all slices in its children node
      node = node.childNodes[0];
    }
    // clear the previous text if any
    this.textArray.forEach(i => {
      if (svg.childNodes[1]) {
        svg.removeChild(svg.childNodes[1]);
      }
    });
    // get all the slices
    const slices: HTMLCollection = node.children;
    for (let i = 0; i < slices.length; i++) {
      // calculate the percentage
      const value = this.data[i].value;
      const percent = Math.floor((value / this.pieDataTotal) * 100);
      if (percent > 10) {
        const text = this.generateText(slices.item(i), percent);
        this.textArray.push(text);
        svg.append(text);
      }
    }
    // this is a timeout, less redraw on div resize
    this.textRedraw = null;
  }

  private generateText(g, value) {
    // get boundaries
    const bbox = g.getBBox();
    let x = bbox.x + bbox.width / 2;
    let y = bbox.y + bbox.height / 2;
    // create text element
    const text = document.createElementNS('http://www.w3.org/2000/svg', 'text');
    // sometimes the text draw on the center of the pie, like when 60%
    // adjust it slightly
    if (x <= 10 && x >= 0) {
      x = 50;
    }
    if (y <= 10 && y >= 0) {
      y = 50;
    }

    text.setAttribute('x', '' + x);
    text.setAttribute('y', '' + y);
    text.setAttribute('fill', 'white');
    text.textContent = value + '%';
    text.setAttribute('text-anchor', 'middle');
    return text;
  }

I used a timeout to redraw the percentage when resize the div.
Here is how it looks:
image

Updates on this? Is there a better way for this?

Are there any updates on this?

Any updates on the issue?

I am also interested in this feature!

Me too!

I've made a hack, it will draw the percentage based on each slice's position.
I've never played with svg before, not sure if it's correct way to do it. At least it works:)
Here is the code:

private drawOnPieChart() {
    // get the ngx chart element
    let node = this.basicPieChart.chartElement.nativeElement;
    let svg;
    for (let i = 0; i < 5; i++) {
      if (i === 3) {
        // this is the pie chart svg
        svg = node.childNodes[0];
      }
      // at the end of this loop, the node should contain all slices in its children node
      node = node.childNodes[0];
    }
    // clear the previous text if any
    this.textArray.forEach(i => {
      if (svg.childNodes[1]) {
        svg.removeChild(svg.childNodes[1]);
      }
    });
    // get all the slices
    const slices: HTMLCollection = node.children;
    for (let i = 0; i < slices.length; i++) {
      // calculate the percentage
      const value = this.data[i].value;
      const percent = Math.floor((value / this.pieDataTotal) * 100);
      if (percent > 10) {
        const text = this.generateText(slices.item(i), percent);
        this.textArray.push(text);
        svg.append(text);
      }
    }
    // this is a timeout, less redraw on div resize
    this.textRedraw = null;
  }

  private generateText(g, value) {
    // get boundaries
    const bbox = g.getBBox();
    let x = bbox.x + bbox.width / 2;
    let y = bbox.y + bbox.height / 2;
    // create text element
    const text = document.createElementNS('http://www.w3.org/2000/svg', 'text');
    // sometimes the text draw on the center of the pie, like when 60%
    // adjust it slightly
    if (x <= 10 && x >= 0) {
      x = 50;
    }
    if (y <= 10 && y >= 0) {
      y = 50;
    }

    text.setAttribute('x', '' + x);
    text.setAttribute('y', '' + y);
    text.setAttribute('fill', 'white');
    text.textContent = value + '%';
    text.setAttribute('text-anchor', 'middle');
    return text;
  }

I used a timeout to redraw the percentage when resize the div.
Here is how it looks:
image

How exactly can I use this?

Any update on this?

Any updates?

Any Updates?

Just bumping this, as I am interested in some updates too.

Any updates?

I've made a hack, it will draw the percentage based on each slice's position.
I've never played with svg before, not sure if it's correct way to do it. At least it works:)
Here is the code:

```
private drawOnPieChart() {
// get the ngx chart element
let node = this.basicPieChart.chartElement.nativeElement;
let svg;
for (let i = 0; i < 5; i++) {
if (i === 3) {
// this is the pie chart svg
svg = node.childNodes[0];
}
// at the end of this loop, the node should contain all slices in its children node
node = node.childNodes[0];
}
// clear the previous text if any
this.textArray.forEach(i => {
if (svg.childNodes[1]) {
svg.removeChild(svg.childNodes[1]);
}
});
// get all the slices
const slices: HTMLCollection = node.children;
for (let i = 0; i < slices.length; i++) {
// calculate the percentage
const value = this.data[i].value;
const percent = Math.floor((value / this.pieDataTotal) * 100);
if (percent > 10) {
const text = this.generateText(slices.item(i), percent);
this.textArray.push(text);
svg.append(text);
}
}
// this is a timeout, less redraw on div resize
this.textRedraw = null;
}

private generateText(g, value) {
// get boundaries
const bbox = g.getBBox();
let x = bbox.x + bbox.width / 2;
let y = bbox.y + bbox.height / 2;
// create text element
const text = document.createElementNS('http://www.w3.org/2000/svg', 'text');
// sometimes the text draw on the center of the pie, like when 60%
// adjust it slightly
if (x <= 10 && x >= 0) {
x = 50;
}
if (y <= 10 && y >= 0) {
y = 50;
}

text.setAttribute('x', '' + x);
text.setAttribute('y', '' + y);
text.setAttribute('fill', 'white');
text.textContent = value + '%';
text.setAttribute('text-anchor', 'middle');
return text;

}

I improved position of the labels, using parametric form of equation of a circle. Someone may be interested in it until there is no such feature in ngx-charts.
(values for my pie chart are already in percents so I don't have calculations of "percent" variable)

private drawOnPieChart() {
    let node = this.pieChart.chartElement.nativeElement;
    let svg: any;
    for (let i = 0; i < 5; i++) {
      if (i === 3) {
        svg = node.childNodes[0]; // pie chart svg
      }
      // at the end of this loop, the node should contain all slices in its children node
      node = node.childNodes[0];
    }
    // clear the previous text if any
    this.pieChartLabels.forEach(i => {
      if (svg.childNodes[1]) {
        svg.removeChild(svg.childNodes[1]);
      }
    });
    const slices: HTMLCollection = node.children;
    let minX = 0;
    let maxX = 0;
    for (let i = 0; i < slices.length; i++) {
      const bbox = (<any>slices.item(i)).getBBox();
      minX = Math.round((bbox.x < minX ? bbox.x : minX) * 10) / 10;
      maxX =
        Math.round(
          (bbox.x + bbox.width > maxX ? bbox.x + bbox.width : maxX) * 10
        ) / 10;
    }

    for (let i = 0; i < slices.length; i++) {
      const percent = this.data[i].value;
      let startingValue = 0;
      for (let j = 0; j < i; j++) {
        startingValue += this.data[j].value;
      }
      if (percent >= 2) {
        const text = this.generateText(percent, maxX - minX, startingValue);
        this.pieChartLabels.push(text);
        svg.append(text);
      }
    }
  }

  private generateText(percent: number, diagonal: number, startingValue: number) {
    // create text element
    const text = document.createElementNS('http://www.w3.org/2000/svg', 'text');
    const r = Math.round(diagonal / 2.5);
    // angle = summed angle of previous slices + half of current slice - 90 degrees (starting at the top of the circle)
    const angle = ((startingValue * 2 + percent) / 100 - 0.5) * Math.PI;
    const x = r * Math.cos(angle);
    const y = r * Math.sin(angle) + 5;

    text.setAttribute('x', '' + x);
    text.setAttribute('y', '' + y);
    text.setAttribute('fill', 'white');
    text.textContent = percent + '%';
    text.setAttribute('text-anchor', 'middle');
    return text;
  }

Any updates yet?

any plan to support this feature ?

waiting for this feature.

Any news to this?

Any news to this?

This issue has been opening for 2 years and no update for this requested feature.

My children, please add this to your angular code to add percentages to the labelling:

setTimeout(function(){
    let labels = $('.pie-label');
    for (let label of labels){
      label.innerHTML += "%";
    }
}, 100)

It's not ideal, but it works.
You are welcome.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

faisalnet5 picture faisalnet5  路  3Comments

cfremgen picture cfremgen  路  3Comments

emeric0101 picture emeric0101  路  3Comments

NashIlli picture NashIlli  路  3Comments

merinshaji picture merinshaji  路  3Comments