Chart.js - Formatting Y axis - string-formatting

I'm using Chart.js to draw a simple bar plot and I need to format its Y axis like
123456.05 to 123 456,05 $
I don't understand how to use scaleLabel : "<%=value%>"
I saw someone pointing to "JS Micro-Templating",
but no clue how to use that with our scaleLabel option.
Does someone know how to format this Y axis, and maybe give me an example ?

I had the same problem, I think in Chart.js 2.x.x the approach is slightly different like below.
ticks: {
callback: function(label, index, labels) {
return label/1000+'k';
}
}
More in details
var options = {
scales: {
yAxes: [
{
ticks: {
callback: function(label, index, labels) {
return label/1000+'k';
}
},
scaleLabel: {
display: true,
labelString: '1k = 1000'
}
}
]
}
}

An undocumented feature of the ChartJS library is that if you pass in a function instead of a string, it will use your function to render the y-axis's scaleLabel.
So while, "<%= Number(value).toFixed(2).replace('.',',') + ' $' %>" works, you could also do:
scaleLabel: function (valuePayload) {
return Number(valuePayload.value).toFixed(2).replace('.',',') + '$';
}
If you're doing anything remotely complicated, I'd recommend doing this instead.

scaleLabel : "<%= Number(value).toFixed(2).replace('.', ',') + ' $'%>"

For anyone using 3.X.X, here's the updated syntax to change y axis labels:
scales: {
y: {
ticks: {
callback: (label) => `$ ${label}`,
},
},
},

Chart.js 2.X.X
I know this post is old. But if anyone is looking for more flexible solution, here it is
var options = {
scales: {
yAxes: [{
ticks: {
beginAtZero: true,
callback: function(label, index, labels) {
return Intl.NumberFormat().format(label);
// 1,350
return Intl.NumberFormat('hi', {
style: 'currency', currency: 'INR', minimumFractionDigits: 0,
}).format(label).replace(/^(\D+)/, '$1 ');
// ₹ 1,350
// return Intl.NumberFormat('hi', {
style: 'currency', currency: 'INR', currencyDisplay: 'symbol', minimumFractionDigits: 2
}).format(label).replace(/^(\D+)/, '$1 ');
// ₹ 1,350.00
}
}
}]
}
}
'hi' is Hindi. Check here for other locales argument
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl#Locale_identification_and_negotiation#locales_argument
for more currency symbol
https://www.currency-iso.org/en/home/tables/table-a1.html

As Nevercom said the scaleLable should contain javascript so to manipulate the y value just apply the required formatting.
Note the the value is a string.
var options = {
scaleLabel : "<%= value + ' + two = ' + (Number(value) + 2) %>"
};
jsFiddle example
if you wish to set a manual y scale you can use scaleOverride
var options = {
scaleLabel : "<%= value + ' + two = ' + (Number(value) + 2) %>",
scaleOverride: true,
scaleSteps: 10,
scaleStepWidth: 10,
scaleStartValue: 0
};
jsFiddle example

Here you can find a good example of how to format Y-Axis value.
Also, you can use scaleLabel : "<%=value%>" that you mentioned, It basically means that everything between <%= and %> tags will be treated as javascript code (i.e you can use if statments...)

Related

custom legend hide() does not remove data labels

I am building a project using React with a doughnut and bar chart. Working with Chart.js 3.xx.
I am trying to make my custom legend functional. I want to make data fractions disappear when the user clicks my legend items - like in the native legend, and optimally also remove the data and make the chart present it's updated data after removal.
I also use data labels to present percentage of the data on the fractions.
import ChartDataLabels from 'chartjs-plugin-datalabels';
I came across this topic: ChartJS - Show/hide data individually instead of entire dataset on bar/line charts
and used this suggested code there:
function chartOnClick(evt) {
let chart = evt.chart
const points = chart.getElementsAtEventForMode(evt, 'nearest', {}, true);
if (points.length) {
const firstPoint = points[0];
//var label = myChart.data.labels[firstPoint.index];
//var value = myChart.data.datasets[firstPoint.datasetIndex].data[firstPoint.index];
let datasetIndex = firstPoint.datasetIndex, index = firstPoint.index;
if (firstPoint.element.hidden != true) {
chart.hide(datasetIndex, index);
} else {
chart.show(datasetIndex, index);
}
}
}
options: { // chart options
onClick: chartOnClick
}
It almost works, but the hide() method doesn't remove the fraction's percentage data label when activated, whereas when clicking the native legend it does remove it entirely.
I tried looking in the plugin's docs but didn't manage to find how to remove a single label.
How can I achieve what I am looking for?
EDIT:
Options Object:
export const doughnutOptsObj = {
onClick: chartOnClick,
responsive: true,
maintainAspectRatio: false,
layout: { padding: { top: 16, bottom: 16 } },
hoverOffset: 32,
plugins: {
legend: {
display: true,
position: 'bottom',
},
datalabels: {
formatter: (value, dnct1) => {
let sum = 0;
let dataArr = dnct1.chart.data.datasets[0].data;
dataArr.map((data) => {
sum += Number(data);
});
let percentage = ((value * 100) / sum).toFixed() + '%';
return percentage;
},
color: ['#fbfcfd'],
font: { weight: 'bold' },
// display: false, <-- this works and makes all of the data labels disappear
},
},
};
It seems that the onClick function is working properly.
I have tried the attached code, leveraging on toggleDataVisibility API, and it's working as requested (codepen: https://codepen.io/stockinail/pen/abKNJqJ):
function chartOnClick(evt) {
let chart = evt.chart
const points = chart.getElementsAtEventForMode(evt, 'nearest', {}, true);
if (points.length) {
const firstPoint = points[0];
chart.toggleDataVisibility(firstPoint.index);
chart.update();
}
}

Se Chartjs horizontal

I want to create a horizontal bar chart like this photo using Chartjs library
So I follow the instructions and I have something like:
TS:
createAreaChart() {
this.barChart1 = document.getElementById('barChart1');
this.ctx1 = this.barChart1.getContext('2d');
let i = 0;
this.data1.forEach(div => {
if(i==0){
this.backgroundColors.push('#A60A2D');
} if(i==1) {
this.backgroundColors.push('#00A2C3');
} if(i==2) {
this.backgroundColors.push('#434F55');
i = -1;
}
i++;
});
this.chart1 = new Chart(this.ctx1, {
type: 'bar',
data: {
labels: this.data1.map(r => r.icon),
datasets: [{
data: this.data1.map(r => r.total),
label: 'Annual Cost',
backgroundColor: this.backgroundColors,
borderColor: this.backgroundColors,
borderWidth: 1
}]
},
options: {
legend: {
display: false
},
tooltips: {
callbacks: {
label: function(tooltipItem, data) {
var value = data.datasets[0].data[tooltipItem.index];
if (parseInt(value) >= 1000) {
return '$' + value.toFixed(2).toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",");
} else {
return '$' + value.toFixed(2).toString();
}
}
}
},
scales: {
yAxes: [{
ticks: {
beginAtZero: true,
precision: 2,
userCallback : function(value, index, values) {
if(parseInt(value) >= 1000){
return '$' + value.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",");
} else {
return '$' + value;
}
}
}
}]
}
}
});
this.chart1.height = 225;
}
HTML
<div class="card chart-card">
<div class="card-header">
<div class="card-title">Test</div>
</div>
<div class="card-body">
<div class="chart">
<canvas id="barChart1" height="220px"></canvas>
</div>
</div>
</div>
But for some reason I get a vertical bar like this:
How can I do to change it to be horizontally orientation, the current Y axis be at X axis and above each bar use current X axis, exactly like the first photo? Regards
UPDATE
As the comment below I currently using Chartjs V 2.9.4, now it works, the chart is oriented horizontally
Now, I need to place Y axis text above each bar, is this possible?
It seems that you're still using Chart.js version 2. The current latest version is 3.5.1.
Change type: 'bar' into type: 'horizontalBar' and it should work.
new Chart(this.ctx1, {
type: 'horizontalBar',
...
For further details, please consult Horizontal Bar Chart from the Chart.js v2.9.4 documentation.
Add this:
options: {
indexAxis: 'y',
...
}
Here is ChartJS documentation

How to insert HTML inside an Ext.each() in the labeFormatter function of legend-EXTJS -highcharts

I was referring to the Highcharts doc but I don't quite understand how to add an HTMl inside an EXT js loop inside the labelFormatter func.
Here is the code:
getLegendConfig: function() {
var ct = this;
return {
align: 'right',
verticalAlign: 'top',
layout: 'vertical',
x: -25,
y: 25,
borderWidth: 0,
floating: type === 'column',
itemMarginBottom: 5,
labelFormatter: function() {
var count = this.y || 0;
Ext.each(ct.data.series, function(ct) {
ct.data.map(function(pt) {
if (pt !== null) {
return [
'<div>',
Ext.util.Format.ellipsis("some name", 48),
'<span class="qx-highchart-legend-item-count">',
Ext.util.Format.number(pt.count, '0,000'),
'</span>',
'</div>'
].join('');
}
})
});
},
useHTML: true
};
}.createDelegate(this)
With the above code, nothing gets populated inside the element, not sure how to get this running. Any ideas?
Here is the fiddle:
https://fiddle.sencha.com/#view/editor&fiddle/2k5v
Thanks
Since you do not have in your fiddle the actual highchart but only the getLegendConfig portion it would be tricky to give exact answer. However this goes back to the previous question you asked about the panel with values. So this is a similar example where the values are rendered on a panel: https://fiddle.sencha.com/#view/editor&fiddle/2k60
Here is the main part:
series.map((x) => {
return x.data.map(function(pt) {
if (pt !== null) {
return [
'<div>',
Ext.util.Format.ellipsis("name", 48),
'<span class="qx-highchart-legend-item-count">',
Ext.util.Format.number(pt.count, '0,000'),
'</span>',
'</div>'
].join('');
}
})
}),
You have to compose that with your code and see if you get the output you want. Based on the example I gave above this will render on a simple panel the values you wanted so it should be working with your pieces. Let me know how it goes.

Can a subgrid be exported in angular ui-grid

I'm using the grid from http://ui-grid.info/ in a project. I've created a hierarchical grid, which works nicely, but when I do an export, it only exports the data from the top-level grid.
This is by design and is standard functionality for the grid, so there's no point in me putting up any example code. Any example from http://ui-grid.info/docs/#/tutorial will do.
Is there a way to export the subgrid (preferably both the main grid AND the subgrid together as they appear in the grid)?
Sadly the answer is no.
As you can see here the function getData iterates through all rows and then through all columns, adding to an array of extractedFields the columns to be extracted and aggregating those in an array of extractedRows.
This means that no data, other than what's defined in gridOptions' columnDef will be read, converted and extracted.
By design, subgrid information are stored inside a property of any row entity's subGridOptions but this property is never accessed inside of the exporter feature.
The motivation behind this behaviour is that expandable grid feature is still at an alpha stage, so, supporting this in other features is not a compelling priority.
Furthermore, adding subgrid to a CSV could be quite hard to design, if we wanted to provide a general solution (for example I don't even think it would be compliant to CSV standard if you had different number of columns in the main grid and in subgrids).
That said, ui-grid is an open source project, so, if you have a working design in mind, feel free to open a discussion about it on the project gitHub page or, even better, if you can design a working (and tested) solution and create a pull request, even better!
I managed to get it working, although if I had the time I would do it a bit better than what I've done by actually creating a branch of the code and doing it properly, but given time constraints, what I've got is working nicely.
FYI, here's the way I ended up getting it to do what I wanted:
In my grid options, I turned off the CSV export options in the grid menu (because I've only implemented the changes for PDF).
I made a copy of exporter.js, named it custom.exporter.js and changed my reference to point to the new file.
In custom.exporter.js, I made a copy of the getData function and named it getGridRows. getGridRows is the same as getData, except it just returns the rows object without all the stuff that gets the columns and so on. For now, I'm coding it to work with a known set of columns, so I don't need all that.
I modified the pdfExport function to be as follows:
pdfExport: function (grid, rowTypes, colTypes) {
var self = this;
var exportData = self.getGridRows(grid, rowTypes, colTypes);
var docContent = [];
$(exportData).each(function () {
docContent.push(
{
table: {
headerRows: 1,
widths: [70, 80, 150, 180],
body: [
[{ text: 'Job Raised', bold: true, fillColor: 'lightgray' }, { text: 'Job Number', bold: true, fillColor: 'lightgray' }, { text: 'Client', bold: true, fillColor: 'lightgray' }, { text: 'Job Title', bold: true, fillColor: 'lightgray' }],
[formattedDateTime(this.entity.JobDate,false), this.entity.JobNumber, this.entity.Client, this.entity.JobTitle],
]
}
});
var subGridContentBody = [];
subGridContentBody.push([{ text: 'Defect', bold: true, fillColor: 'lightgray' }, { text: 'Vendor', bold: true, fillColor: 'lightgray' }, { text: 'Status', bold: true, fillColor: 'lightgray' }, { text: 'Sign off', bold: true, fillColor: 'lightgray' }]);
$(this.entity.Defects).each(function () {
subGridContentBody.push([this.DefectName, this.DefectVendor, this.DefectStatus, '']);
});
docContent.push({
table: {
headerRows: 1,
widths: [159, 150, 50, 121],
body: subGridContentBody
}
});
docContent.push({ text: '', margin: 15 });
});
var docDefinition = {
content: docContent
}
if (self.isIE()) {
self.downloadPDF(grid.options.exporterPdfFilename, docDefinition);
} else {
pdfMake.createPdf(docDefinition).open();
}
}
No, there is no direct way to export subgrid. rather you can create youur own json data to generate csv file.
Please check the below code
function jsonToCsvConvertor(JSONData, reportTitle) {
//If JSONData is not an object then JSON.parse will parse the JSON string in an Object
var arrData = typeof JSONData !== 'object' ? JSON.parse(JSONData) : JSONData,
csv = '',
row,
key1,
i,
subGridData;
//Set Report title in first row or line
csv += reportTitle + '\r\n\n';
row = '';
for (key1 in arrData[0]) {
if(key1 !== 'subGridOptions' && key1 !== '$$hashKey'){
row += key1 + ',';
}
}
csv += row + '\r\n';
for (i = 0; i < arrData.length; i++) {
row = '';
subGridData = '';
for (key1 in arrData[i]) {
if(key1 !== 'subGridOptions' && key1 !== '$$hashKey'){
row += '"' + arrData[i][key1] + '",';
}
else if(key1 === 'subGridOptions'){
//csv += row + '\r\n';
subGridData = writeSubGridData(arrData[i][key1].data);
}
}
csv += row + '\r\n';
csv = subGridData ? csv + subGridData + '\r\n' : csv;
}
if (csv === '') {
console.log('Invalid data');
}
return csv;
}
//Generates subgrid Data to exportable form
function writeSubGridData(subgridData){
var j,
key2,
csv = '',
row = '';
for (key2 in subgridData[0]){
if(key2 !== '$$hashKey'){
row += key2 + ',';
}
}
csv = row + '\r\n';
for (j=0; j < subgridData.length ; j++){
row = '';
for(key2 in subgridData[j]){
if(key2 !== '$$hashKey'){
row += '"' + subgridData[j][key2]+ '",';
}
}
csv += row + '\r\n';
}
return csv;
}
jsonToCsvConvertor(exportData, 'New-Report');

highcharts : set title on exporting

I'm looking a way to:
hide title on the HTML page result
show title on the highcharts graph when I export it (PDF,PNG,JPEG or print)
I don't know how to proceed. There is someone able to help me?
You can define this parameter in exporting.
http://api.highcharts.com/highcharts#exporting.chartOptions
http://jsfiddle.net/BdHJM/
exporting:{
chartOptions:{
title: {
text:'aaaaa'
}
}
},
put this function in your document ready function below is a code for changing highcharts print prototype and just for the patch or to make it work put rangeSelector option in your exporting and set it to false as mentioned below you can set it to your needs in future
Highcharts.wrap(Highcharts.Chart.prototype, 'print', function (proceed) {
var applyMethod = function (whatToDo, margin) {
this.extraTopMargin = margin;
this.resetMargins();
this.setSize(this.container.clientWidth , this.container.clientHeight , false);
this.setTitle(null, { text: 'SET TITLE HERE' :'});
this.rangeSelector.zoomText[whatToDo]();
$.each(this.rangeSelector.buttons, function (index, button) {
button[whatToDo]();
});
};
if (this.rangeSelector) {
var extraMargin = this.extraTopMargin;
applyMethod.apply(this, ['hide', null]);
var returnValue = proceed.call(this);
applyMethod.apply(this, ['show', extraMargin]);
this.setTitle(null, { text: '' });
} else {
return proceed.call(this);
this.setTitle(null, { text: '' });
this.yAxis[0].setExtremes();
} }
});
and in chart option set this (change it according to you need to, i am just putting my code for reference
)
exporting: {
scale: 1,
sourceWidth: 1600,
sourceHeight: 900,
chartOptions: {
rangeSelector: {
enabled: false
},
}

Resources