Issue with displaying data labels in Chart.js when using a custom formatter

I'm having trouble getting the datalabels plugin to work with Chart.js. No labels are showing up on my graph, even though I am using version 2.9.0 of Chart.js.

Here is my code:

var ctx = this.pieChartRef.nativeElement;
    this.pieChart = new Chart(ctx, {
      type: 'pie',
      data: {
        labels: Object.keys(pieData),
        datasets: [
          {
            data: Object.values(pieData),
            backgroundColor: random,
          },
        ],
      },
      options: {
        legend: {
          position: 'bottom',
          display: true,
        },
        plugins: {
          datalabels: {
            color: '#fff',
            display: true,
            formatter: function (value, ctx) {
              let sum = 0;
              let dataArr: number[] = ctx.chart.data.datasets[0]
                .data as number[];
              dataArr.map((data) => {
                sum += data;
              });
              return ((value * 100) / sum).toFixed(2) + '%';
            },
          },
        },
      },
    });

Even when I simplify the formatter code to just return value + '%';, still no datalabels appear. Can anyone offer some guidance? I'm at a loss!

Appreciate any help in advance.

Answer №1

Initially, the issue at hand is not tied to specific versions (like 2.7 versus 2.9, etc.). In essence, it would be beneficial to include a demo.

It seems like you aim to have the data [5,5] displayed as 50%, 50% and [3,3,3] represented as 33% / 33% / 33%.

Your problem appears to revolve around calculations.

The sum seems "buggy" (log its value to the console). The callback runs more than once (once for each label) - leading to repetitive array declarations inside a loop.

let dataArr: number[] = ctx.chart.data.datasets[0].data as number[];
dataArr.map((data) => {
  sum += data;
});

This sample functions properly (without nested loops):

var pieChart = new Chart(ctx, {
  type: 'pie',
  data: {
    labels: ['January', 'February'],
    datasets: [{
      label: 'My First dataset',
      data: [5, 10],
      backgroundColor: ["red", "blue"]
    }]
  },
  options: {
    legend: {
      position: 'bottom',
      display: true,
    },
    plugins: {
      datalabels: {
        color: '#fff',
        display: true, 
        formatter: function (value, ctx) {
          return ((value * 100) / total(ctx)).toFixed(2) + '%';
        },
      },
    },
  },
})

function total(chart){
  let data = chart.chart.data.datasets[0].data;
  const reducer = (accumulator, currentValue) => accumulator + currentValue;
  var total = data.reduce(reducer);
  return total;
}
<div id="wrapper">
  <canvas id="ctx" width="500" height="450"></canvas>
</div>

<script src="https://cdn.jsdelivr.net/npm/<a href="/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="e4878c859690ca8e97a4d6cadccad4">[email protected]</a>"></script>

<script src="https://cdn.jsdelivr.net/npm/<a href="/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="fe9d969f8c8a948dd38e928b999790d39a9f8a9f929f9c9b928dbeced0c9d0ce">[email protected]</a>/dist/chartjs-plugin-datalabels.min.js"></script>

DRY

The code above also exhibits some degree of repetitiveness - by declaring the data outside the chart.js object, you could compute the data total just once.

var data = [4, 9, 5, 2];
/* calculate total */
const reducer = (accumulator, currentValue) => accumulator + currentValue;
var total = data.reduce(reducer);

var pieChart = new Chart(ctx, {
  type: 'pie',
  data: {
    labels: ['January', 'February', 'March', 'April'],
    datasets: [{
      label: 'My First dataset',
      data: data,
      backgroundColor: ["red", "blue", "green", "violet"]
    }]
  },
  options: {
    legend: {
      position: 'bottom',
      display: true,
    },
    plugins: {
      datalabels: {
        color: '#fff',
        display: true, 
        formatter: function (value, ctx) {
          return ((value * 100) / total).toFixed(2) + '%'; 
        },
      },
    },
  },
})
<div id="wrapper">
  <canvas id="ctx" width="500" height="450"></canvas>
</div>

<script src="https://cdn.jsdelivr.net/npm/<a href="/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="accfc4cdded882c6dfec9e8294829c">[email protected]</a>"></script>

<script src="https://cdn.jsdelivr.net/npm/<a href="/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="7013181102041a035d001c0517191e5d141104111c1112151c0330405e475e40">[email protected]</a>/dist/chartjs-plugin-datalabels.min.js"></script>

To make it more DRY/modular (for changing data), encapsulate the total data in a function and so forth.

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

What is the way to declare a prop as optional in Svelte using TypeScript?

I am facing an issue in declaring an optional prop in Svelte with TypeScript. The error message I receive is "Declaration or statement expected". Can someone guide me on how to correctly declare the prop? Definition of My Type: export enum MyVariants { ...

Tips on sending parameter values in a request to a server

return this.http.post<User>(`${environment.apiUrl}/api/users/LoginUser/`, { UserName, Password }) When making requests to the server, I am encountering an issue where the parameter values are coming back as null despite my attempts to pass them alon ...

Having trouble retrieving the parent object in Angular OnInit method?

I am facing an issue with attaching a custom validator to an Angular Form Control. The Form Controls are initialized in the ngOnInit method, and I want the validator to check if a field has input only when a boolean class member this.shouldCheck is true. ...

I am looking to include both a space and a comma in the State dropdown value in an Angular application

I am facing an issue with my dropdown in Angular 9. When the State is more than one, it displays without any space between them. I want it to show like: Licensed State:ARCA I need like: AR, CA This is the code I have so far: <ng-c ...

Looking to refine the search in a table for numerical values instead of strings?

I am encountering an issue with searching in my table as I can only search by Strings. To address this, I have implemented a filter pipe: export class FilterPipe implements PipeTransform { transform(items: any[], field: string, value: string): any[ ...

Utilize TypeScript to import a JSON file

I am trying to incorporate a JSON file using TypeScript, which contains data about different regions in Italy along with their respective capitals. Here is a snippet of the data: { "italia": [ { "regione": "Abruzzo", "capoluoghi": [ ...

Transitioning Ionic2 RC0 - relocating custom libraries to the build directory after the removal of gulp

https://i.sstatic.net/0KLac.png The Gulp tool has been removed by the Ionic Team from their project. What is the alternative method to set up libraries? ...

What could be causing the problem between typescript and Prisma ORM as I attempt to locate a distinct item?

I'm encountering a problem with using the prisma .findUnique() function. Even though my code doesn't display any compilation errors, attempting to access a product page triggers a runtime error. PrismaClientValidationError: Invalid `prisma.produ ...

Should I write out the typescript .d.ts file by hand or generate it automatically

When using Typescript, you can utilize the "declaration": true" option in tsconfig to automatically generate d.ts files from your existing Typescript code. Although they may not be as concise as manually written ones, I am curious if there is any downside ...

Show the string representation of the enum instead of its numerical value

Can someone help me with this issue? I am trying to retrieve the role using get role(): string but it's not returning the full role name. For example, instead of getting "Head Administrator", I only get "Administrator" returned. I know that Role["Admi ...

Passing properties from a parent component to a child component in a React TypeScript application

I'm currently facing an issue with passing props to my component. It seems like I am unable to pass the 'Commune' due to it having a name property. Does anyone have any suggestions on how I can pass Commune.name as a prop to my component? ...

Struggle with Transmitting Information in Ionic 2

I have been working on an Ionic project that involves a JSON file with preloaded data. The initial page displays a list from which a user can select an item to view its content. However, I am encountering a "Response with status: 404 Not Found for URL" err ...

Error Type: When attempting to subscribe to the notification event service, a TypeError occurred in the code

component file ngOnInit(): void { this.subscribeNotification(); } subscribeNotification { this.notificationEventService.showNotification.subscribe((data: NotificationData) => { console.log(data); }); } Service file import { EventEmitter, Injec ...

Set the input cursor to the next input in Angular. Note that the next input tag will be dynamically added when the Enter key is pressed in the previous input

Implementing a for loop with input tags, the user can input text and submit it by pressing enter. This action adds another input tag dynamically to the current loop. The challenge is to move the cursor to the next input after submission, even though the ne ...

Tips for utilizing generics to determine the data type of a property by its name

I am seeking a method to determine the type of any property within a class or a type. For instance, if I had the classes PersonClass and PersonType, and I wanted to retrieve the type of the nationalId property, I could achieve this using the following cod ...

What could be causing my Angular application to go blank when I activate a rotation animation in my component.ts file?

<button [@trigger]="menuState" class="text-white font-bold cursor-pointer text-3xl leading-none px-3 py-1 border-transparent rounded bg-transparent block lg:hidden outline-none focus:outline-none ml-24 md:ml-96" type="button" id="menu" (click)="toggleMe ...

In the Next.js environment, the utilization of chartjs's cutoutPercentage and tooltips features may not

For my next.js project, I am utilizing the react-chartjs-2 library to generate charts. I have encountered an issue where the cutoutPercentage property in chart.js, which is supposed to make the donut chart thinner, does not seem to work properly in next. ...

Interacting with an iframe within the same domain

I'm currently working on an application in Angular 6 that requires communication with an iframe on the same origin. I'm exploring alternative methods to communicate with the iframe without relying on the global window object. Is there a more effi ...

Lazy loading in Angular allows you to navigate directly to a certain feature component

I'm currently working on implementing lazy loading in Angular 6, and I want to include links on my main homepage that direct users to specific feature components. Here is the hierarchy of my project: app.module.ts |__homepage.component.ts |__options ...

The imagemin code runs successfully, however, there appears to be no noticeable impact

I've been attempting to compress an image in Node 14 using Typescript and the imagemin library. After following some online examples, I executed the code but it seems that nothing is happening as there are no errors reported upon completion. The outp ...