Forcetrails: Bar Chart In Lightning Web Component Using Chartjs

Bar Chart in Lightning Web Component
Bar Chart in Lightning Web Component

Data visualization is the very important part of today's Data Science era, the executives at any company loves to see the visual representation of the data, being a web standard Lightning web components gives you the ability to show charts and graphs using external libraries like ChartJs, D3 Js.

In this post, I am going to demonstrate, how you can leverage the power of ChartJs to build a responsive Bar chart in Lwc. My last post on Using ChartJs in Lwc was just a simple example with some static data, in this post, I am going to demonstrate how you can get data from Opportunity and feed it to the bar chart.

Let us assume the scenario where you want to visualize opportunity amounts of the Closed Won opportunities for the last 30 days.

Create a lightning web component named 'chart', this is the generic ChartJs implementation. You can create all different types of charts supported by ChartJs by passing different chart configurations from its parent component.

1. Download this ChartJs file(Open link -> Right Click -> Save As) and upload it into a static resource with the name ''ChartJs".


2. Create a new Lwc with the name 'chart' and copy the below codes in respective files.

chart.js
1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
import {LightningElement, api, track} from 'lwc';
import chartjs from '@salesforce/resourceUrl/ChartJs';
import {loadScript} from 'lightning/platformResourceLoader';
import {ShowToastEvent} from 'lightning/platformShowToastEvent';

export default class Chart extends LightningElement {
 @api loaderVariant = 'base';
 @api chartConfig;

 @track isChartJsInitialized;
 renderedCallback() {
  if (this.isChartJsInitialized) {
   return;
  }
  // load static resources.
  Promise.all([loadScript(this, chartjs)])
   .then(() => {
    this.isChartJsInitialized = true;
    const ctx = this.template.querySelector('canvas.barChart').getContext('2d');
    this.chart = new window.Chart(ctx, JSON.parse(JSON.stringify(this.chartConfig)));
    this.chart.canvas.parentNode.style.height = 'auto';
    this.chart.canvas.parentNode.style.width = '100%';
   })
   .catch(error => {
    this.dispatchEvent(
     new ShowToastEvent({
      title: 'Error loading ChartJS',
      message: error.message,
      variant: 'error',
     })
    );
   });
 }
}
In the above code, I have declared two @api attributes in the chart component

  • loaderVariant - Used to set the loader variant from the parent component.
  • chartConfig - Used to pass the chart settings and data from the parent component, so that we can reuse this component for different charts.

In the renderedCallback,

I have put the Chartjs source file in the static resource, with the help of platformResourceLoader loaded it in the component.

Initialized the chart js once the Chart.bundle.min.js is loaded.
chart.html
<template>
    <div class="slds-p-around_small slds-grid slds-grid--vertical-align-center slds-grid--align-center">
        <canvas class="barChart" lwc:dom="manual"></canvas>
        <div if:false={isChartJsInitialized} class="slds-col--padded slds-size--1-of-1">
            <lightning-spinner alternative-text="Loading" size="medium" variant={loaderVariant}></lightning-spinner>
        </div>
    </div>
</template>
In the HTML I have added a canvas tag, the Chartjs library uses that canvas to draw the chart.

3. Now, create an apex class to fetch the Opportunity Data.

public class OpportunityChartCntrl {
    @AuraEnabled(cacheable=true)
    public static List<Opportunity> getOpportunities(){
        return [SELECT Amount,Name,StageName FROM Opportunity WHERE CloseDate >= LAST_N_DAYS:30 AND StageName = 'Closed Won'];
    }
}

4. Create another lwc, here you can use your desired component. I have named it as opportunityBarChart.

opportunityBarChart.js
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
import {LightningElement, wire, track} from 'lwc';
import getOpportunities from '@salesforce/apex/OpportunityChartCntrl.getOpportunities';
export default class OpportunityBarChart extends LightningElement {
 @track chartConfiguration;

 @wire(getOpportunities, {})
 getOpportunities({error, data}) {
  if (error) {
   this.error = error;
   console.log('error => ' + JSON.stringify(error));
   this.chartConfiguration = undefined;
  } else if (data) {
   let chartData = [];
   let chartLabels = [];
   data.forEach(opp => {
    chartData.push(opp.Amount);
    chartLabels.push(opp.Name);
   });

   this.chartConfiguration = {
    type: 'bar',
    data: {
     labels: chartLabels,
     datasets: [
      {
       label: 'Closed Won Last Week',
       barPercentage: 0.5,
       barThickness: 6,
       maxBarThickness: 8,
       minBarLength: 2,
                            backgroundColor: "green",
       data: chartData,
      },
     ],
    },
    options: {
    },
   };
   console.log('data => ', data);
   this.error = undefined;
  }
 }
}
The wire method fetches the opportunity data and feeds into the chart lwc.

The most important thing, all the chart behavior is based on the config you set to chart. All chart settings and data will be set using

chartConfiguration
It contains three main things 
1. type: It defines the type of chart being rendered, here we have 'bar'.
2. data: Here we pass the chartlabels and chart data and other settings for the bar chart.
3. options: additional settings for Charts.



opportunityBarChart.html
<template>
    <lightning-card title="Opportunity Bar Chart" icon-name="utility:chart">
        <template if:true={chartConfiguration}>
            <c-chart chart-config={chartConfiguration}></c-chart>
        </template>
    </lightning-card>
</template>
In the HTML we have called the chart component and set the chart-config parameter.

So this is it, we are ready with a bar chart in lwc.
I have put all this code on GitHub repo. Feel free to download it.

Let me know if you have any questions in the comments.
Let me know if you face any issues with this.

Your suggestions are always welcome!

Happy Coding!!

More stuff coming up, please subscribe, keep visiting!


21 comments:
  1. I'm getting the following error when running the code: Cannot read property 'parentNode' of undefined

    How would I resolve this?

    ReplyDelete
    Replies
    1. Hello dear Unknown, the error seems to be in chart.js file, on line number 21. There can be two problems, either the canvas is not found or the chart config is not valid. Can you check if your chart.html file has canvas with class="barChar".

      Also check if the static resource is loaded successfully to be able to init the chart

      Delete
    2. Also please make sure that you have uploaded this chart js file into static resources with name = "ChartJs"(This is case sensitive). Let me know if this helps.

      Also if possible can you share your code on github, so I can take a look?

      Delete
    3. I have made some changes in the source code, please get the updated code.

      Delete
    4. Hi, sorry, I'm not sure why I'm coming through as "Unknown". :D
      Where are the changes? I don't see any in the github repo. You're talking about here, right: https://github.com/rahulgawale/chartjs-lwc/commits/master

      Delete
    5. This is something weird with blogger comments, one of my friend also facing the same issue.

      Anyway, I have updated the code in github too. Actually this is very small change on line 16 of chartjs.js file(point 2 in this post). I have removed the relative path from the static resource. you can see the difference here. Sorry for the inconvenience and thank you for your patience.

      Peace!!

      Delete
    6. So the issue with the error I got is that I was referencing the chart.js file in a zip file, and not in its own static resource? Very strange.

      Delete
    7. Yeah, initially when I built this, I was using the whole zip file from chartjs later, I learned that only one file is needed from that zip, so remove the others, I did the required changed in my own source code but, forgot to update that in my blog post and git repo.

      Delete
  2. hello, how we do if we want click bar and get eventt ?

    ReplyDelete
    Replies
    1. Hello Dear Unknown!! Yes you can do that, but you will need to play around it. I haven't tried that yet. Still this link from Stack Overflow have multiple options to do that.

      Delete
    2. Hello Rahul Gwale thx for your response, i have fine this example (https://jsfiddle.net/1gzhnv37/)but I can't transpose it to LWC.

      Delete
    3. Hi, I tried this in lwc but did not work... may be we need to try with latest version of chartjs

      Delete
  3. Hi Rahul i got it it's work for me.

    ReplyDelete
  4. Error loading ChartJS
    Cannot read property 'parentNode' of undefined

    ReplyDelete
    Replies
    1. Did you add the canvas to the html? make sure you have the correct id of the canvas to the queryselector at line 21 of chart.js. also you can share you code with me on github issues.

      Delete
  5. Hi Rahul, Bar chart is not displaying for me in Lightning App. I have copied the same code from your blog except the filter clause in the apex class. I placed the component in the App builder, to test the component, but the chart is not displayed, in the console i am able to see the data is retrieved from the apex class. Can you please let me know what might be the error.

    ReplyDelete
    Replies
    1. Please check if the chartjs script is getting loaded from the static resource. Try setting log in renderedCallback. Once you get the chart is loaded plug in the data in it. Let me know if this helps. Thanks!

      Delete
  6. Thank you for this wonderful job, it is helpful

    ReplyDelete
    Replies
    1. You are most welcome FOCO, I am glad that you found this helpful!

      Delete

Hi there, comments on this site are moderated, spams and promotions will be deleted. If you have any concern, or if you are not able to comment for some reason, reach email us at rahul@forcetrails.com