Doesn't seem to work with ng2-charts
I installed the plugin as specified in the docs, I am using ng2-charts with following options:
public barChartOptions = { plugins: { stacked100: { enable: true }, }, legend: { display: true, position: 'bottom', }, scales: { xAxes: [{ display: true, stacked: true }], yAxes: [{ display: false, stacked: true }], }, };
No percentages are shown for me. The chart still displays value options.
hi @jyotidhiman0610 did you find any solution i am also using ng2-charts but my charts didn't stacked at 100 %
ng2-charts has some additional fiddling to make plugin registration work correctly, once it is hooked in correctly the stacked charts do work.
You'll want to take a look at this issue discussion: https://github.com/valor-software/ng2-charts/issues/752#issuecomment-439060266
This is not a solution but you found a quick solution for use this lib with angular :
- Create new file
stacked-100.lib.ts
import { Chart } from 'chart.js';
/**
* Chart.JS and Angular can be incompatible.
* This function is inspired of lib : https://github.com/y-takey/chartjs-plugin-stacked100
*
* Add custom plugins for stacked bar with 100% Y axe.
*/
export const chartJsRegistererStacked100 = () => {
const isObject = function (obj) {
const type = typeof obj;
return type === 'object' && !!obj;
};
const dataValue = function (dataPoint, isHorizontal) {
if (isObject(dataPoint)) {
return isHorizontal ? dataPoint.x : dataPoint.y;
}
return dataPoint;
};
const cloneArray = function (srcAry) {
const dstAry = [];
const length = srcAry.length;
for (let i = 0; i < length; i++) {
dstAry.push(srcAry[i]);
}
return dstAry;
};
const setOriginalData = function (data) {
data.originalData = data.datasets.map(function (dataset) {
return cloneArray(dataset.data);
});
};
// set calculated rate (xx%) to data.calculatedData
const calculateRate = function (data, isHorizontal, precision) {
const visibles = data.datasets.map(function (dataset) {
if (!dataset._meta) {
return true;
}
for (const i in dataset._meta) {
return !dataset._meta[i].hidden;
}
});
let datasetDataLength = 0;
if (data && data.datasets && data.datasets[0] && data.datasets[0].data) {
datasetDataLength = data.datasets[0].data.length;
}
const totals = Array.apply(null, new Array(datasetDataLength)).map(function (el, i) {
return data.datasets.reduce(function (sum, dataset, j) {
const key = dataset.stack;
if (!sum[key]) {
sum[key] = 0;
}
sum[key] += Math.abs(dataValue(dataset.data[i], isHorizontal)) * visibles[j];
return sum;
}, {});
});
data.calculatedData = data.datasets.map(function (dataset, i) {
// tslint:disable-next-line:no-shadowed-variable
return dataset.data.map(function (val, i) {
const total = totals[i][dataset.stack];
const dv = dataValue(val, isHorizontal);
return dv && total ? round(dv / total, precision) : 0;
});
});
};
const getPrecision = function (pluginOptions) {
// return the (validated) configured precision from pluginOptions or default 1
const defaultPrecision = 1;
if (!pluginOptions.hasOwnProperty('precision')) {
return defaultPrecision;
}
if (!pluginOptions.precision) {
return defaultPrecision;
}
const optionsPrecision = Math.floor(pluginOptions.precision);
if (isNaN(optionsPrecision)) {
return defaultPrecision;
}
if (optionsPrecision < 0 || optionsPrecision > 16) {
return defaultPrecision;
}
return optionsPrecision;
};
const round = function (value, precision) {
const multiplicator = Math.pow(10, precision);
return Math.round(value * 100 * multiplicator) / multiplicator;
};
const tooltipLabel = function (isHorizontal) {
return function (tooltipItem, data) {
const datasetIndex = tooltipItem.datasetIndex;
const index = tooltipItem.index;
const originalValue = data.originalData[datasetIndex][index];
const rateValue = data.calculatedData[datasetIndex][index];
return '' + rateValue + '% (' + dataValue(originalValue, isHorizontal) + ')';
};
};
const reflectData = function (srcData, datasets) {
if (!srcData) {
return;
}
srcData.forEach(function (data, i) {
datasets[i].data = data;
});
};
// tslint:disable-next-line:no-shadowed-variable
const isHorizontalChart = function (chartInstance) {
return chartInstance.config.type === 'horizontalBar';
};
// tslint:disable-next-line:no-shadowed-variable
const Stacked100Plugin = {
id: 'stacked100',
// tslint:disable-next-line:no-shadowed-variable
beforeInit: function (chartInstance) {
if (!chartInstance.options.stacked100 || !chartInstance.options.stacked100.enable) {
return;
}
const xAxes = chartInstance.options.scales.xAxes;
const yAxes = chartInstance.options.scales.yAxes;
const isVertical = chartInstance.config.type === 'bar' || chartInstance.config.type === 'line';
[xAxes, yAxes].forEach(function (axes) {
axes.forEach(function (hash) {
hash.stacked = true;
});
});
(isVertical ? yAxes : xAxes).forEach(function (hash) {
if (!hash.ticks.min) {
const hasNegative = chartInstance.data.datasets.some(function (dataset) {
return dataset.data.some(function (value) {
return value < 0;
});
});
hash.ticks.min = hasNegative ? -100 : 0;
}
if (!hash.ticks.max) {
hash.ticks.max = 100;
}
});
// Replace tooltips
if (chartInstance.options.stacked100.hasOwnProperty('replaceTooltipLabel') && !chartInstance.options.stacked100.replaceTooltipLabel) {
return;
}
chartInstance.options.tooltips.callbacks.label = tooltipLabel(isHorizontalChart(chartInstance));
},
// tslint:disable-next-line:no-shadowed-variable
beforeDatasetsUpdate: function (chartInstance) {
if (!chartInstance.options.stacked100 || !chartInstance.options.stacked100.enable) {
return;
}
setOriginalData(chartInstance.data);
const precision = getPrecision(chartInstance.options.stacked100);
calculateRate(chartInstance.data, isHorizontalChart(chartInstance), precision);
reflectData(chartInstance.data.calculatedData, chartInstance.data.datasets);
},
// tslint:disable-next-line:no-shadowed-variable
afterDatasetsUpdate: function (chartInstance) {
if (!chartInstance.options.stacked100 || !chartInstance.options.stacked100.enable) {
return;
}
reflectData(chartInstance.data.originalData, chartInstance.data.datasets);
}
};
if (!(Chart.pluginService as any).getAll().filter(p => p.id === 'stacked100')[0]) {
Chart.pluginService.register(Stacked100Plugin);
}
};
- and on your chart component :
ngAfterViewInit() {
const canvas: any = document.getElementById('canvas_' + this.guid);
const ctx: HTMLCanvasElement = canvas.getContext('2d');
// bug fix chartjs/angular. registerer stacked100 plugins
chartJsRegistererStacked100();
const options: any | Chart.ChartConfiguration = {
type: this.chart.type,
data: this.chart.data,
options: {
stacked100: {
enable: true,
replaceTooltipLabel: true,
}
},
};
const myBar = new Chart(ctx, options);
}
/!\ chartjs-plugin-stacked100's options is not on "plugins" attribut but on "options" attribut !