Bar Chart in Lightning Web Component |
Data visualization is a very important part of today's Data Science era, the executives at any company love 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 the respective files.
chart.js1 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', }) ); }); } } |
- 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>
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.
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 most important thing, all the chart behavior is based on the config you set to the 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>
So this is it, we are ready with a bar chart in lwc.
I have put all this code on the 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!
I'm getting the following error when running the code: Cannot read property 'parentNode' of undefined
ReplyDeleteHow would I resolve this?
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".
DeleteAlso check if the static resource is loaded successfully to be able to init the chart
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.
DeleteAlso if possible can you share your code on github, so I can take a look?
I have made some changes in the source code, please get the updated code.
DeleteHi, sorry, I'm not sure why I'm coming through as "Unknown". :D
DeleteWhere 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
This is something weird with blogger comments, one of my friend also facing the same issue.
DeleteAnyway, 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!!
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.
DeleteYeah, 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.
Deletehello, how we do if we want click bar and get eventt ?
ReplyDeleteHello 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.
DeleteHello Rahul Gwale thx for your response, i have fine this example (https://jsfiddle.net/1gzhnv37/)but I can't transpose it to LWC.
DeleteHi, I tried this in lwc but did not work... may be we need to try with latest version of chartjs
DeleteHi Rahul i got it it's work for me.
ReplyDeletegreat !! what was thr problem?
Deletei call config inside @wire that's why!
DeleteError loading ChartJS
ReplyDeleteCannot read property 'parentNode' of undefined
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.
DeleteHi 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.
ReplyDeletePlease 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!
DeleteThank you for this wonderful job, it is helpful
ReplyDeleteYou are most welcome FOCO, I am glad that you found this helpful!
DeleteHello,
ReplyDeletePlease, I have some issues; I added the following code in options for shorthanding the millions (For example for 1000000 it displays 1M in yAxis), but the callback function doesn't run:
options: {
scales: {
yAxes: [{
ticks: {
beginAtZero:true,
// Shorthand the millions
callback: function(value, index, values) {
console.log('====IN Function '+JSON.stringify(value) );
return value / 1e6 + 'M';
}
}
}],
xAxes: [{
ticks: {
}
}]
}
}
Help please!
Thanks in advance
Hi FOCO, Welcome back here! Actually I also had faced the similar issue in one of my demo project but could not solve that. I also wanted to add the custom ticks on the axis. Meanwhile I didn't look into that due to some other priorities, at this point I can't really help immediately (Until the weekend). Till then please see this "ChartJS Axes Callback not working". Appreciate your patience!
DeleteHi Rahul,
DeleteThank for your feedback.
I continue looking for the solution.
Hi FOCO, I appreciate your patience. I have the working example for that with Chart.js v2.8.0. You can try the same code with latest version too. Here is the git repo for that. chartjs-with-custom-ticks-lwc. Let me know if this helps you. For the code check in the chartExample.js file.
DeleteI try it and I will give you my feedback as soon as possible.
DeleteThank you Rahul
This comment has been removed by the author.
ReplyDeleteHi Rahul,
ReplyDeleteIn the above Image Y axis is getting Amount,& i have tried it but i am getting o.1,0.2....etc. I need in the Y axis top show Amount or another field
can you share your code? If you want to add label for y axis that you can specify in the options. Also the axis values are based on the data so please verify the data.
DeleteHi Rahul,
ReplyDeleteI used currency field so it was resolved now & now how to display the numbers in chart
Hi Santhosh, I never tried putting text on the bar, can you try solution from this link?
DeleteThank you Rahul for this article. Really helpful.
ReplyDeleteI used the same but getting error on below line
this.template.querySelector('canvas.barChart').getContext('2d'); (from chart.js)
Cannot read property 'getContext' of null. All my code is same. except the data. Appreciate any help
Hello Dear Unknown, Did you add the class barChart to the canvas like this
Delete<canvas class="barChart" lwc:dom="manual"></canvas>
If you still have any problem, please share your code on GitHub gist or raise an issue here
Thanks I opened an issue on github site.
DeletePlease ignore the post. Everything working fine. I was using wrong Chart.js (I used the chat.bundle.min.js as you suggested and able to get the chart. Thanks again for the nice article on charts.
DeleteNo problem, always happy to help.
DeleteHello Rahul , I need this chart in horizontal manner. How could I change this?
ReplyDeleteHi Dear Unknown, Try change the type to 'horizontalBar' on line #21 from opportunityBarChart.js file.
DeleteHi Rahul!
ReplyDeleteWhen adding LWC to a tab I get the error.
Validation Errors While Saving Record(s)
There were custom validation error(s) encountered while saving the affected record(s). The first validation error encountered was "We couldn't validate your component c:opportunityBarChart used in Custom Tab. Review it and try again.".
Do you know the solution?
Sorry for my English, thanks.
Hi Edwir, You have to mention the target as lightning__Tab in the meta.xml file of your component, then you can add that to tab.
ReplyDeleteWhat is the difference between
ReplyDeletehttps://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.9.4/Chart.js From W3schools and
https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.8.0/Chart.bundle.min ?
Can your code use the Chart.js library from W3, or is there more stuff in the "this ChartJS" download?
Only the difference is the version of chart js, for that you need to check if it works with lwc. Some of the features were not working because of lightning locker, but you can definitely give it a try with latest version of chartjs
DeleteHi,I noticed an issue with the displayed graph where one data value is significantly larger (2147) compared to the other (2). Due to the large difference in scale, the lower value might not be visually represented in the graph. and if the value is zero xaxis the graph ranges from (-5 to +5) in yaxis .
DeleteHi Renu, in that case you need to use logarithmic scale instead of linear scale. Also try to explore the options in chart js scale configurations.
DeleteOnly the difference is the version of chart js, for that you need to check if it works with lwc. Some of the features were not working because of lightning locker, but you can definitely give it a try with latest version of chartjs
ReplyDeleteHi,I noticed an issue with the bar graph where one data value is significantly larger (2147) compared to the other (2). Due to the large difference in scale, the lower value might not be visually represented accurately in the graph. how can i fix this issue? is there any option to adjust the axes of the graph ? if there is no value ,graph ranges from negative to positive (-5 to +5 in Yaxis) .
ReplyDeleteHi Rahul!
ReplyDeleteI couldn't pass the Apex class List values (correctly obtained according to the developer console's log) to the JS file (data appears as an array with the proper number of records, but each record is empty!). The chart displays only null values...
Hi Bernardo, Good day, can you please share your relevant code snippets so that I can check what is the issue with the code? Please post the GitHub link or you can send it to me at rahul@forcetrails.com
Delete