0

I'm trying to integrate the Chart.js datalabels plugin into a Lightning Web Component (LWC) for creating customizable bar charts in my Salesforce application. I have followed the provided code example, but I'm encountering some issues with getting the datalabels to display properly. Here's a summary of what I've done:

  1. Imported the Chart.js and Chart.js datalabels plugin as static resources in my Salesforce org.
  2. Initialized Chart.js and the datalabels plugin using loadScript in the renderedCallback of my LWC.
  3. Added the datalabels plugin to the list of plugins when initializing the chart: plugins: [ChartDataLabels].
  4. Customized datalabels appearance and behavior inside the options object under plugins.datalabels.

Despite following these steps, the datalabels are not displaying as expected. Can anyone provide guidance on how to properly integrate the Chart.js datalabels plugin into an LWC for creating bar charts with labeled data points? Any insights, code examples, or alternative approaches would be greatly appreciated. Thank you!

import { LightningElement, api } from 'lwc';
import chartjs from '@salesforce/resourceUrl/Chartjs_last';

import { loadScript } from 'lightning/platformResourceLoader';
import { ShowToastEvent } from 'lightning/platformShowToastEvent';
import ChartDataLabels from '@salesforce/resourceUrl/chartjs_datalabels';


export default class Gen_barchart extends LightningElement {
@api chartData;
@api type
@api maintainAspectRatio = false

isChartJsInitialized;
renderedCallback() {

    if (this.isChartJsInitialized) {
        return;
    }

    if (this.chartData) {
        // load chartjs from the static resource
        Promise.all([loadScript(this, chartjs),
        loadScript(this, ChartDataLabels)])



            .then(() => {

                console.log('this.ChartDataLabels', ChartDataLabels)

                let datasets
                let chartLabel = []
                let max
                let tempMax

                console.log('this.type', this.type)

                if (this.type === 'number') {

                    let chartGoals = [];
                    let chartSuccess = [];

                    this.chartData.forEach(record => {
                        let goalNum = record.goal == null ? 0 : Math.round(Number(record.goal))
                        chartGoals.push(goalNum);
                        let successNum = record.success == null ? 0 : Math.round(Number(record.success))
                        chartSuccess.push(successNum);
                        chartLabel.push(record.missionDetail);
                    });

                    if (chartGoals.length > 0 && chartSuccess.length > 0) {
                        let goalMax = Math.max(...chartGoals)
                        let successMax = Math.max(...chartSuccess)
                        tempMax = Math.max(goalMax, successMax)
                    }

                    datasets = [{
                        label: 'Goal Count',
                        backgroundColor: "#79D1CF",
                        data: chartGoals,                        
                    },
                    {
                        label: 'Success Count',
                        backgroundColor: "#D1CF79",
                        data: chartSuccess,
                    },
                    ]
        }


                max = tempMax == null ? 100 : Math.ceil(tempMax + tempMax * 0.3)
                max = max === 0 ? 100 : max
                this.isChartJsInitialized = true;
                const ctx = this.template.querySelector('canvas.barChart').getContext('2d');

                var moveChart = {

                    id: 'moveChart',
                    afterEvent: function (chartInstance, args) {
                        const ctx = chartInstance.ctx;
                        const canvas = chartInstance.canvas;
                        const chartArea = chartInstance.chartArea;
                        const left = chartArea.left,
                            right = chartArea.right,
                            top = chartArea.top,
                            bottom = chartArea.bottom,
                            width = right - left,
                            height = bottom - top;

                        canvas.addEventListener('mousemove', function (e) {
                            const rect = canvas.getBoundingClientRect();
                            const x = e.clientX - rect.left;
                            const y = e.clientY - rect.top;

                            if (x >= left - 15 && x <= left + 15 && y >= top && y <= bottom) {
                                canvas.style.cursor = 'pointer';
                            } else if (x >= right - 15 && x <= right + 15 && y >= top && y <= bottom) {
                                canvas.style.cursor = 'pointer';
                            } else {
                                canvas.style.cursor = 'default';
                            }
                        });
                    },


                    afterDraw: function (chartInstance) {

                        const ctx = chartInstance.ctx
                        const chartArea = chartInstance.chartArea
                        const left = chartArea.left,
                            right = chartArea.right,
                            top = chartArea.top,
                            bottom = chartArea.bottom,
                            width = right - left,
                            height = bottom - top;

                        class CircleChevron {
                            draw(ctx, x1, pixel) {

                                const angle = Math.PI / 180;
                                ctx.beginPath();
                                ctx.lineWidth = 3;
                                ctx.strokeStyle = '#79D1CF';
                                ctx.fillStyle = 'white';

                                ctx.arc(x1, height / 2 + top, 15, angle * 0, angle * 360, false);
                                ctx.stroke();
                                ctx.fill();
                                ctx.closePath();

                                //chevron Arrow Left
                                ctx.beginPath();
                                ctx.lineWidth = 3;
                                ctx.strokeStyle = '#79D1CF';
                                ctx.fillStyle = 'white';
                                ctx.moveTo(x1 + pixel, height / 2 + top - 7.5);
                                ctx.lineTo(x1 - pixel, height / 2 + top);
                                ctx.lineTo(x1 + pixel, height / 2 + top + 7.5);
                                ctx.stroke();
                                ctx.fill();
                                ctx.closePath();

                            }

                        }

                        const drawCircleLeft = new CircleChevron();
                        drawCircleLeft.draw(ctx, left, 5);

                        const drawCircleRight = new CircleChevron();
                        drawCircleRight.draw(ctx, right, -5);


                    }

                }






                this.chart = new window.Chart(ctx, {
                    type: "bar",


                    plugins: [ChartDataLabels, moveChart],

                    options: {
                        layout: {
                            padding: {
                                right: 18,
                            }
                        },
                        // onComplete: chart => {
                        //     const ctx = chart.ctx;
                        //     console.log(ctx, 'ctx')
                        //     chart.data.datasets.forEach((dataset, i) => {
                        //         const meta = chart.getDatasetMeta(i);
                        //         meta.data.forEach((bar, index) => {
                        //             const data = dataset.data[index];
                        //             console.log('data', data)
                        //             ctx.fillStyle = 'black';
                        //             ctx.textAlign = 'center';
                        //             ctx.textBaseline = 'bottom';
                        //             ctx.fillText(data, bar.x, bar.y - 5);
                        //         });
                        //     });
                        // },

                        responsive: true,

                        maintainAspectRatio: this.maintainAspectRatio,
                        plugins: {
                            legend: {
                                position: "bottom",

                            },
                            datalabels: {
                                color: "orange",
                                labels: {
                                    title: {
                                        font: {
                                            weight: "bold"
                                        }
                                    }
                                }
                            },
                            title: {
                                display: true,
                            },


                        },
                        tooltips: {
                            enabled: true
                        },

                        scales: {
                            x: {
                                min: 0,
                                max: 3,
                                grid: {
                                    display: false,
                                },
                                ticks: {
                                    display: true,

                                },
                            },
                            y: {
                                beginAtZero: true,

                            }

                        },


                    },
                    data: {
                        datasets: datasets,
                        labels: chartLabel,
                    },

                });


                function moveScroll(chartInstance) {
                    console.log('ok')
                    const ctx = chartInstance.ctx
                    const chartArea = chartInstance.chartArea
                    const canvas = chartInstance.canvas
                    const left = chartArea.left,
                        right = chartArea.right,
                        top = chartArea.top,
                        bottom = chartArea.bottom,
                        width = right - left,
                        height = bottom - top;

                    canvas.addEventListener('click', function (event) {

                        const rect = canvas.getBoundingClientRect();
                        const x = event.clientX - rect.left;
                        const y = event.clientY - rect.top;
                        if (x >= left - 20 && x <= left + 20 && y >= height / 2 + top - 20 && y <= height / 2 + top + 20) {


                            chartInstance.options.scales.x.min = chartInstance.options.scales.x.min - 4 //max size +1 
                            chartInstance.options.scales.x.max = chartInstance.options.scales.x.max - 4  //max size +1 

                            if (chartInstance.options.scales.x.min <= 0) {
                                chartInstance.options.scales.x.min = 0
                                chartInstance.options.scales.x.max = 3  //max size 
                            }

                        }

                        if (x >= right - 20 && x <= right + 20 && y >= height / 2 + top - 20 && y <= height / 2 + top + 20) {


                            chartInstance.options.scales.x.min = chartInstance.options.scales.x.min + 4  //max size +1
                            chartInstance.options.scales.x.max = chartInstance.options.scales.x.max + 4 //max size +1

                            if (chartInstance.options.scales.x.max >= chartInstance.data.labels.length) {
                                chartInstance.options.scales.x.min = chartInstance.data.labels.length - 4 //max size +1
                                chartInstance.options.scales.x.max = chartInstance.data.labels.length
                            }

                        }
                        chartInstance.update()


                    })
                }


                this.chart.ctx.onclick = moveScroll(this.chart)




            })
            .catch(error => {
                console.log(error)
                if (!error.message.includes('Canvas is already in use')) {
                    this.dispatchEvent(
                        new ShowToastEvent({
                            title: 'Error loading Chart',
                            message: error.message,
                            variant: 'error',
                        })
                    );
                }
            });
    }
}

}

1 Answer 1

0

When I had the chart.js and datalabels.js loading in parallel, the initialization was unreliable because datalabels.js depends on chart.js so chart.js has to be loaded first. So I'm now loading them sequentially (using async/await in place of raw promises but nested promises would work too):

            await loadScript(this, resource + '/Chart.js');
            await loadScript(this, resource + '/chartjs-plugin-labels.min.js');

One example config:

            plugins: {
                // Segment labels
                labels: {
                    render: (args) => {
                        return this.formatCompactPercentageValue(args.value);
                    },
                    fontColor: CHART_VALUE_COLOR,
                    fontSize: CHART_VALUE_FONT_SIZE,
                    fontStyle: CHART_VALUE_FONT_STYLE
                },
3
  • Thank you. It worked! Commented Mar 14 at 4:25
  • can you please tell me the versions of chartjs and datalabels plugin you used? Commented Mar 25 at 17:03
  • Hi @MudassirIftikhar, chartjs 2.7.3 and chartjs-plugin-labels.min.js 1.1.0.
    – Keith C
    Commented Mar 26 at 13:17

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .