Adding formatter to Donut/Pie Chart label causes JavaScript Error
Closed this issue · 4 comments
Describe the bug
Adding a formatter via com.github.appreciated.apexcharts.config.plotoptions.pie.builder.ValueBuilder.withFormatter(String) causes JavaScript error "TypeError: (0 , t.value.formatter) is not a function".
Steps or code example to Reproduce
Sample Code:
private static Labels buildLabels() {
final String formatter = "function doSomeJavaScript(x) { " +
" return x.toString().replace(/\\B(?=(\\d{3})+(?!\\d))/g, \",\");" +
"}";
return LabelsBuilder.get()
.withShow(true)
.withName(NameBuilder .get()
.withColor(ColourCodes.BLACK)
.withFontSize(LABEL_FONT_PX)
.build())
.withValue(ValueBuilder .get()
.withFormatter(formatter)
.build())
.build();
}
Expected behavior
A formatted number should appear in the middle of the pie chart e.g. 1,000,000
I have the same issue while trying to set a formatter on the total label of donut chart.
I am setting the following formatter:
function(w) {
w.globals.seriesTotals.reduce((a, b) => a + b, 0);
}
I think the problem here is that the Java wrapper sends the formatter value as a String while JavaScript side expects a function.
I don't think it's possible to send functions via JSON directly since JSON is not supposed to transfer functions: https://stackoverflow.com/questions/2001449/is-it-valid-to-define-functions-in-json-results
A workaround is needed: e.g. you'll need to attach the functions beforehand, say to the window
object:
window.formatLabelTotal = function() { ... }
Then the wrapper code would accept the formatter Strings as function names, such as window.formatLabelTotal
. However this needs to be implemented.
I am able to set a formatter on other chart/label and it works.
For example, I do this
private val jvmMemoryChart = ApexChartsBuilder.get()
.withChart(ChartBuilder.get()
.withType(Type.bar)
.build())
.withPlotOptions(PlotOptionsBuilder.get()
.withBar(BarBuilder.get()
.withHorizontal(false)
.withColumnWidth("55%")
.build())
.build())
.withTitle(TitleSubtitleBuilder.get().withText("JVM Memory").build())
.withDataLabels(DataLabelsBuilder.get()
.withEnabled(true)
.withFormatter(jvmMemFormatterJsFucntion)
.build())
[...]
With this formatter string:
private val jvmMemFormatterJsFucntion =
"""
function(val, {seriesIndex, dataPointIndex, w}) {
return (parseFloat(val).toFixed(2)) + " kB";
}
""".trimIndent()
When digging in the wrapper's JS code, this snippet must be the reason it works:
if (this.dataLabels) {
this.config.dataLabels = JSON.parse(this.dataLabels);
if (this.config.dataLabels.formatter) {
this.config.dataLabels.formatter = this.evalFunction(this.config.dataLabels.formatter);
}
}
The evalFunction() does the trick.
Also, by looking at the wrapper's JS code, I don't see anywhere that it handles the plotOptions Pie/Donut label configurations and evalutate the formatter's string. It is just not implemented for all types of label.
Excellent find! I can indeed see at apexcharts-wrapper.js:175
that the plotOptions formatter for pie/donut is not post-processed. Perhaps something like this should be in place:
if (this.plotOptions.pie && this.plotOptions.pie.donut && this.plotOptions.pie.donut.labels && this.plotOptions.pie.donut.labels.total && this.plotOptions.pie.donut.labels.total.formatter) {
this.plotOptions.pie.donut.labels.total.formatter = this.evalFunction(this.plotOptions.pie.donut.labels.total.formatter);
}
(the same for this.plotOptions.pie.donut.labels.value
).