NickyTheWrench
NickyTheWrench

Reputation: 3230

Charts.js Formatting Y Axis with both Currency and Thousands Separator

I'm using Charts.js to show a graph on my site. Currently, the label shows as a long string of numbers (i.e 123456). I want it to show as currency with thousands separator: (i.e $123,456)

I'm using the scaleLabel option to put a $ USD symbol before the value:

scaleLabel: "<%= ' $' + Number(value)%>"

and a function to insert the comma separator:

function(label){return label.value.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",");}

I just do not know how to use these together to get what I want.

Here is the fiddle: http://jsfiddle.net/vy0yhd6m/79/

(please keep in mind that currently the graph will only work if you remove one of those two pieces of JavaScript cited above)

Thank you for any and all help.

Upvotes: 84

Views: 106100

Answers (10)

Hugh Stimson
Hugh Stimson

Reputation: 43

The answers here by Perry Tew, AJ A. et al yield a

Invalid scale configuration for scale: yAxes

error for me. I'm guessing that's because of a change in version 3.

Per the current docs this slight change seems to work:

options: {
    scales: {
        y: {
            ticks: {
                callback: function (value, index, values) {
                    return value.toLocaleString("en-CA", {
                        style: "currency", 
                        currency: "CAD", 
                        minimumFractionDigits:0, 
                        maximumFractionDigits:0});
                }
            }
        }
    }
}

Upvotes: 1

NickyTheWrench
NickyTheWrench

Reputation: 3230

If you are using Charts.js for Angular 2+ (ng2-charts) you can use CurrencyPipe. Here is how I formatted the label:

Inject the dependency within your page.ts file:

import { CurrencyPipe } from '@angular/common';

Here is how I call it within my chart options:

public chartOptions: any = {
        responsive: true,
        legend: {
            display: false,
            labels: {
                display: false
            }
        },
        tooltips: {
          enabled: true,
          mode: 'single',
          callbacks: {
            label: function(tooltipItem, data) {
              let label = data.labels[tooltipItem.index];
              let datasetLabel = data.datasets[tooltipItem.datasetIndex].data[tooltipItem.index];
              let currencyPipe = new CurrencyPipe('en');
              let formattedNumber = currencyPipe.transform(datasetLabel, 'USD', 'symbol');
              return label + ': ' + formattedNumber;
            }
          }
        }
    };

UPDATE 2022-04-07: The syntax has changed for Chart.js version 3. Here is what the options object looks like if you are using v3:

import { ChartConfiguration, ChartData, ChartType } from 'chart.js';
import { CurrencyPipe } from '@angular/common';

public chart_options: ChartConfiguration['options'] = {
   layout: {
      padding: 25,
   },
   responsive: true,
   plugins: {
      legend: {
         display: false,
      },
      tooltip: {
         enabled: true,
         callbacks: {
            label: function(context) {
               let currency_pipe = new CurrencyPipe('en');
               return ' ' + context.label + ': ' + currency_pipe.transform(context.parsed, 'USD', 'symbol');
            }
         }
      }
   }
};

public chart_type: ChartType = 'doughnut';
public chart_labels: string[] = [];
public chart_data: ChartData<'doughnut'> = {
   labels: this.chart_labels,
   datasets: [{
      data: [],
      backgroundColor: [],
   }]
};

<div style="display: block;">
   <canvas baseChart [data]="chart_data" [options]="chart_options" [type]="chart_type"></canvas>
</div>

Check out the Chart.js v3 Migration Guide for more info

Upvotes: 6

Jaison James
Jaison James

Reputation: 4552

I know my answer is too late but due to op are getting more attention this may relevant now.

Here is the more simple and decent approach.

const formatter = new Intl.NumberFormat("en-US", {
    style: "currency",
    currency: "USD"
}); // Change locale according to your currency and country

var options = {
    scales: {
        yAxes: [
            {
                ticks: {
                    callback: (label, index, labels) => {
                        return formatter.format(label);
                    }
                }
            }
        ]
    }
}

Upvotes: 4

jumpjack
jumpjack

Reputation: 990

There is a specific javascript function to convert a long number into a number formatted according to system settings: toLocaleString().

You can specify that the label of each tick (or of a specific tick identified by its number index) must be built by your own function, by adding "callback:" keyword inside tick options:

Before:

        ticks: {
                  max: maxAltitude,
                  min: 0
                }

After:

        ticks: {
                  max: maxAltitude,
                  min: 0, // <--- dont' foget to add this comma if you already have specified ticks options
                    callback:  
                      function(value, index, valuesArray) {
                          // value: currently processed tick label
                          // index of currently processed tick label
                          // valuesArray: array containing all predefined labels
                          return  value.toLocaleString(); 
                      } // Function end
                } // Ticks options end

Without the comments and without unused variables:

        ticks: {
                  max: maxAltitude,
                  min: 0, 
                  callback:  
                      function(value) {
                        return  value.toLocaleString(); 
                      }
                }

Upvotes: -1

Christhofer Natalius
Christhofer Natalius

Reputation: 3388

Using chartjs v2.8.0, after looking around the docs, I found it here.

Instead of making my own formatter, I'm using numeraljs to format the number. So this is what I do:

import numeral from 'numeral'

options: {
  scales: {
    yAxes: [{
      ticks: {
        callback: function (value, index, values) {
          // add comma as thousand separator
          return numeral(value).format('0,0')
        },
      }
    }]
  },
  tooltips: {
    callbacks: {
      label: function (tooltipItem, data) {
        var label = data.datasets[tooltipItem.datasetIndex].label || ''

        if (label) {
          label += ': '
        }
        label += numeral(tooltipItem.yLabel).format('0,0')
        return label
      },
    },
  },
}

You can use format('$ 0,0') to add currency symbol along with comma thousand separator.

Upvotes: 5

Son Dang
Son Dang

Reputation: 371

In chartjs v2.0, you can set a global options like this:

Chart.defaults.global.tooltips.callbacks.label = function(tooltipItem, data) {
    return tooltipItem.yLabel.toLocaleString("en-US");
};

Chart.scaleService.updateScaleDefaults('linear', {
    ticks: {
        callback: function (value, index, values) {
            return value.toLocaleString();
        }
    }
});

Upvotes: 8

Perry Tew
Perry Tew

Reputation: 2632

I'm new to chart.js, but here's what I had to do to make Billy Moon's answer work with the latest version 2.1.6.

  var data = {
    labels: ["12 AM", "1 AM", "2 AM", "3 AM", "4 AM", "5 AM", "6 AM", "7 AM", "8 AM", "9 AM", "10 AM", "11 AM", "12 PM", "1 PM", "2 PM", "3 PM", "4 PM", "5 PM", "6 PM", "7 PM", "8 PM", "9 PM", "10 PM", "11 PM"],
    datasets: [
      {
        label: "Sales $",
        lineTension: 0,
        backgroundColor: "rgba(143,199,232,0.2)",
        borderColor: "rgba(108,108,108,1)",
        borderWidth: 1,
        pointBackgroundColor: "#535353",
        data: [65, 59, 80, 81, 56, 55, 59, 80, 81, 56, 55, 40, 59, 80, 81, 56, 55, 40, 59, 80, 81, 56, 55, 40]
      }
    ]
  };

  //var myChart =
  new Chart(document.getElementById('sales-summary-today'), {
    type: 'line',
    data: data,
    options: {
      animation: false,
      legend: {display: false},
      maintainAspectRatio: false,
      responsive: true,
      responsiveAnimationDuration: 0,
      scales: {
        yAxes: [{
          ticks: {
            beginAtZero: true,
            callback: function(value, index, values) {
              if(parseInt(value) >= 1000){
                return '$' + value.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",");
              } else {
                return '$' + value;
              }
            }
          }
        }]
      }
    }
  });

Again, credit goes to Billy Moon's Answer for the label formatting function.

Upvotes: 119

AJ A.
AJ A.

Reputation: 646

I'm mostly summarizing what others have mentioned, but I think the cleanest solution to this exact (and frequently encountered) question is to utilize the toLocaleString method with USD currency formatting:

return value.toLocaleString("en-US",{style:"currency", currency:"USD"});

This works in all modern browsers. The Mozilla documentation for toLocaleString lists specific browser compatibility and options for other locales, currencies, and formatting types (e.g. percentages).

Note Chart.js Version 2+ (released in April 2016) requires using the callback method for advanced tick formatting:

var chartInstance = new Chart(ctx, {
  type: 'line',
  data: data,
  options: {
     scales: {
       yAxes: [{
         ticks: {
           callback: function(value, index, values) {
             return value.toLocaleString("en-US",{style:"currency", currency:"USD"});
           }
         }
       }]
     }
   }
 });

The syntax if you are using Chart.js Version 1.X would be:

var myLineChart = new Chart(ctx).Line(data, options);
var data = {
  ...
}
var options = {
  scaleLabel: function(label) {
    return value.toLocaleString("en-US",{style:"currency", currency:"USD"});
}

Credit to Perry Tew for referencing the syntax change, and to mfink for the idea to use toLocaleString.

Upvotes: 52

Ege Ersoz
Ege Ersoz

Reputation: 6571

Adding to Perry Tew's answer, if you have negative dollar amounts on your axes (e.g. when displaying a profit/loss chart), you can use this:

ticks: {
    callback: function(value, index, values) {
        if(parseInt(value) > 999){
            return '$' + value.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",");
        } else if (parseInt(value) < -999) {
            return '-$' + Math.abs(value).toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",");
        } else {
            return '$' + value;
        }
    }
}

The correct format for displaying negative currencies is -$XXX, so we prepend -$ to the value, and then run it through Math.abs(), which converts it to positive.

Upvotes: 10

Billy Moon
Billy Moon

Reputation: 58521

You should be able to include currency prefix in composition of label inside function...

var options = {
    animation: false,
    scaleLabel:
    function(label){return  '$' + label.value.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",");}
};

http://jsfiddle.net/vy0yhd6m/80/

Upvotes: 43

Related Questions